export interface Lens<T, V> {
get(o: T): V;
set(v: V, o: T): T;
}
export const identity = <T>(): Lens<T, T> => identityLens
const identityLens = ({
get: (o) => o,
set: (v, o) => v
})
export const property = <T, K extends keyof T>(k: K): Lens<T, T[K]> => ({
get: (o: T): T[K] => o[k],
set: (v: T[K], o: T): T => ({ ...o as any, [k]: v })
})
export const properties = <T>(): { [K in keyof T]: Lens<T, T[K]> } => propertiesProxy
const propertiesProxy = new Proxy(null, {
get(o, k) { return property(k as string); }
});
export 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);
}
});
type inner = { x: number };
const x = property<inner, "x">("x");
type outer = { y: inner };
const y = property<outer, "y">("y");
const u = x.get({x:4})
const u2 = compose(y, x);
const innerLens = properties<inner>()
const u3 = compose(properties<outer>().y, properties<inner>().x)