Last active
December 11, 2018 14:53
-
-
Save freddi301/44647114e52eed89e3a2fc52290558d0 to your computer and use it in GitHub Desktop.
Lens in typescript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const get = Symbol("get"); | |
export const set = Symbol("set"); | |
interface Lens<T, V> { | |
[get]: (o: T) => V; | |
[set]: (v: V) => (t: T) => T; | |
} | |
const identity = <T>(): Lens<T, T> => ({ | |
[get]: t => t, | |
[set]: v => t => v | |
}); | |
const property = <T, K extends keyof T>(k: K): Lens<T, T[K]> => ({ | |
[get]: (t: T): T[K] => t[k], | |
[set]: (v: T[K]) => (t: T): T => ({ ...t, [k]: v }) | |
}); | |
type PropertiesLens<P, T> = Lens<P, T> & | |
{ [K in keyof T]: PropertiesLens<P, T[K]> }; | |
export const properties = <T>(): PropertiesLens<T, T> => | |
propertiesRecursive(identity()); | |
const propertiesRecursive = <P, T>(p: Lens<P, T>): PropertiesLens<P, T> => | |
new Proxy(p, { | |
get(o, k) { | |
if (k === get) return p[get]; | |
if (k === set) return p[set]; | |
else if (typeof k === "string") | |
return propertiesRecursive( | |
compose( | |
p, | |
property(k as keyof T) | |
) | |
); | |
else return (o as any)[k]; | |
} | |
}) as PropertiesLens<P, T>; | |
const compose = <A, B, C>(x: Lens<A, B>, y: Lens<B, C>): Lens<A, C> => ({ | |
[get]: (a: A) => y[get](x[get](a)), | |
[set]: (c: C) => (a: A) => { | |
const b: B = x[get](a); | |
const b2: B = y[set](c)(b); | |
return x[set](b2)(a); | |
} | |
}); | |
// well typed examples | |
type inner = { x: number }; | |
const innerX = property<inner, "x">("x"); | |
type outer = { y: inner }; | |
const outerY = property<outer, "y">("y"); | |
const num = innerX[get]({ x: 4 }); | |
const outerYinnerX = compose( | |
outerY, | |
innerX | |
); | |
const innerLens = properties<inner>(); | |
const inlineCcomposition = compose( | |
properties<outer>().y, | |
properties<inner>().x | |
); | |
const outerSample = { y: { x: 4 } }; | |
console.log([ | |
inlineCcomposition[get](outerSample), | |
inlineCcomposition[set](8)(outerSample) | |
]); | |
console.log([ | |
properties<outer>().y[get](outerSample), | |
properties<outer>().y.x[set](8)(outerSample) | |
]); | |
const outerYInnerX = properties<outer>().y.x; | |
const outerLens = properties<outer>(); | |
console.log([ | |
outerLens.y[get](outerSample), | |
outerLens.y.x[set](8)(outerSample) | |
]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment