All Projects → hatashiro → Lens.ts

hatashiro / Lens.ts

Licence: mit
TypeScript Lens implementation with property proxy

Programming Languages

typescript
32286 projects

Labels

Projects that are alternatives of or similar to Lens.ts

Accessors.jl
Update immutable data
Stars: ✭ 73 (-38.66%)
Mutual labels:  lens
plenopticam
Light-field imaging application for plenoptic cameras
Stars: ✭ 111 (-6.72%)
Mutual labels:  lens
Ef S Adapter
https://twitter.com/JanHenrikH/status/1317520812761546753
Stars: ✭ 69 (-42.02%)
Mutual labels:  lens
BitLens
🔎 Have your bits and eat them too! A C++17 bit lens container for vector types.
Stars: ✭ 20 (-83.19%)
Mutual labels:  lens
coredux
Dualism to Redux. Two-way combining of redux modules
Stars: ✭ 14 (-88.24%)
Mutual labels:  lens
Partial.lenses
Partial lenses is a comprehensive, high-performance optics library for JavaScript
Stars: ✭ 846 (+610.92%)
Mutual labels:  lens
AssetLens
Asset Lens : Unity asset reference management utility working in background. 에디터 백그라운드에서 관리되는 유니티 에셋 파일 레퍼런스 데이터 도구.
Stars: ✭ 47 (-60.5%)
Mutual labels:  lens
Setfield.jl
Update deeply nested immutable structs.
Stars: ✭ 90 (-24.37%)
Mutual labels:  lens
lens
Mirror of Apache Lens
Stars: ✭ 57 (-52.1%)
Mutual labels:  lens
Prolens
👓 Profunctor based lightweight implementation of Lenses
Stars: ✭ 63 (-47.06%)
Mutual labels:  lens
optic
An Erlang/OTP library for reading and updating deeply nested immutable data.
Stars: ✭ 34 (-71.43%)
Mutual labels:  lens
ad-lens
Automatic Differentiation using Pseudo Lenses. Neat.
Stars: ✭ 16 (-86.55%)
Mutual labels:  lens
Steamvr Undistort
SteamVR lens distortion adjustment utility for spherical lenses
Stars: ✭ 33 (-72.27%)
Mutual labels:  lens
putting-lenses-to-work
A presentation for BayHac 2017 on how I uses lenses at work
Stars: ✭ 73 (-38.66%)
Mutual labels:  lens
Lensfunpy
📷 Lens distortion correction for Python, a wrapper for lensfun
Stars: ✭ 83 (-30.25%)
Mutual labels:  lens
lenticular.ts
(Yet another) implementation of functional lenses in JavaScript/TypeScript.
Stars: ✭ 29 (-75.63%)
Mutual labels:  lens
React Image Magnify
A responsive image zoom component designed for shopping sites.
Stars: ✭ 391 (+228.57%)
Mutual labels:  lens
Monocle
Optics library for Scala
Stars: ✭ 1,357 (+1040.34%)
Mutual labels:  lens
Microstates
Composable state primitives for JavaScript
Stars: ✭ 1,312 (+1002.52%)
Mutual labels:  lens
Attic Lens
Mirror of Apache Lens
Stars: ✭ 58 (-51.26%)
Mutual labels:  lens

lens.ts travis-ci

TypeScript Lens implementation with property proxy

Lens?

Lens is composable abstraction of getter and setter. For more detail of Lens, I recommend reading the following documents.

Install

Via npm:

npm i lens.ts

Usage

// import a factory function for lens
import { lens } from 'lens.ts';

type Person = {
  name: string;
  age: number;
  accounts: Array<Account>;
};

type Account = {
  type: string;
  handle: string;
};

let azusa: Person = {
  name: 'Nakano Azusa',
  age: 15,
  accounts: [
    {
      type: 'twitter',
      handle: '@azusa'
    },
    {
      type: 'facebook',
      handle: 'nakano.azusa'
    }
  ]
};

// create an identity lens for Person
let personL = lens<Person>();

// key lens with k()
personL.k('name') // :: Lens<Person, string>
personL.k('accounts') // :: Lens<Person, Array<Account>>
personL.k('hoge') // type error, 'hoge' is not a key of Person
personL.k('accounts').k(1) // :: Lens<Person, Account>
personL.k(1) // type error, 'i' cannot be used for non-array type

// You can use property proxy to narrow lenses
personL.name // :: Lens<Person, string>
personL.accounts // :: Lens<Person, Array<Account>>
personL.accounts[1] // :: Lens<Person, Account>
personL.hoge // type error

// get and set with Lens
personL.accounts[0].handle.get()(azusa) // -> '@azusa'
personL.accounts[0].handle.set('@nakano')(azusa) // -> { ... { handle: '@nakano' } ... }
personL.age.set(x => x + 1)(azusa) // -> { age: 16, ... }

// Lens composition
let fstAccountL = lens<Person>().accounts[0] // :: Lens<Person, Account>
let handleL = lens<Account>().handle // :: Lens<Account, string>
fstAccountL.compose(handleL) // :: Lens<Person, string>

// Getter/Setter composition
fstAccountL.get(handleL.get())(azusa) // -> '@azusa'
fstAccountL.set(handleL.set('@nakano'))(azusa) // -> { ... { handle: '@nakano' } ... }

You can find similar example code in /test.

API

lens.ts exports the followings:

import {
  lens,
  Getter,
  Setter,
  Lens
} from 'lens.ts';

lens

A function lens is a factory function for a lens. Without any arguments except for a type parameter, it returns an identity lens for the provided type.

lens<Person>() // :: Lens<Person, Person>

You can provide a getter and a setter to create a lens manually. They should have proper Getter and Setter types.

let getter // :: Getter<X, Y>
let setter // :: (val: Y) => Setter<X>
lens(getter, setter) // :: Lens<X, Y>

Getter, Setter

They are just a type alias for the following function types.

export type Getter<T, V> = (target: T) => V;
export type Setter<T>    = (target: T) => T;

Basically, Getter is a function to retrieve a value from a target. Setter is a function to set or update a value in a provided target and return a new object with a same type as the target.

Any Setter returned from Lens has immutable API, which means it doesn't modify the target object.

Lens<T, U>

A lens is consisted of a getter and a setter for a source type T and a result type U.

Lens is not a class, so it can't be created with new Lens(). It's internally a product type of LensImpl and LensProxy. Please use lens() to create a lens.

Lens provides the following methods.

.k<K extends keyof U>(key: K)

Narrow the lens for a property or an index of U.

// we will use these types for the following examples
type Person = {
  name: string;
  age: number;
  accounts: Account[];
};

lens<Person>().k('name') // :: Lens<Person, string>
lens<Person>().k('accounts') // :: Lens<Person, Account[]>
lens<Person>().k('accounts').k(0) // :: Lens<Person, Account>

.get()

It is polymorphic.

  • .get(): Getter<T, U>
  • .get<V>(f: Getter<U, V>): Getter<T, V>

.get() returns a getter, which can be applied to an actual target object to retrive an actual value. You can optionally provide another getter (or mapping function) to retrieve a mapped value.

let target = { age: 15, ... };

let ageL = lens<Person>().k('age');

ageL.get()(target) // -> 15
ageL.get(age => age + 10)(target) // -> 25

.set()

It is polymorphic.

  • .set(val: U): Setter<T>
  • .set(f: Setter<U>): Setter<T>

.set() returns a setter, which can set or update an internal value and returns an updated (and new) object. Setters here should be all immutable. You can provide a value to set, or optionally a setter for value.

let target = { age: 15, ... };

let ageL = lens<Person>().k('age');

ageL.set(20)(target) // -> { age: 20, ... }
ageL.set(age => age + 1)(target) // -> { age: 16, ... }

.compose(another: Lens<U, V>): Lens<U, V>

Compose 2 lenses into one.

let lens1: Lens<T, U>;
let lens2: Lens<U, V>;

let accountsL = lens<Person>().k('accounts');
let firstL = <T>() => lens<T[]>().k(0);

let firstAccountL =
  accountsL.compose(firstL()); // :: Lens<Person, Account>

FYI: The reason firstL becomes a function with <T> is to make it polymorphic.

Proxied properties

Lens<T, U> also provides proxied properties for the type U.

objL.name // same as objL.k('name')
arrL[0] // same as arrL.k(0)

Note that proxy objects are not available in Internet Explorer as caniuse describes.

Credits

Property proxy couldn't have been implemented without @ktsn's help.

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].