Skip to content

Instantly share code, notes, and snippets.

@wajda
Created November 7, 2019 08:46
Show Gist options
  • Save wajda/5232c96e05a4c265c2c9b0df5cd9f61c to your computer and use it in GitHub Desktop.
Save wajda/5232c96e05a4c265c2c9b0df5cd9f61c to your computer and use it in GitHub Desktop.
export abstract class Lens<A, B> {
protected constructor(
public get: (A) => B | undefined,
public set: (A, B) => A) {
}
public andThen<C>(inner: Lens<B, C>) {
return new CompositeLens<A, B, C>(this, inner)
}
public focus<C>(path: string): Lens<A, C> {
return path
.split(".")
.reduce(
(lens, prop) => lens.andThen(new PropertyLens(prop)),
this as any) as Lens<A, C>
}
public project(...innerLenses: Lens<B, any>[]) {
return new ProjectorLens(this, innerLenses)
}
}
class ProjectorLens<A, B, X> extends Lens<A, X[]> {
constructor(abLens: Lens<A, B>, bxLens: Lens<B, X>[]) {
super(
(a: A) => {
const b: B | undefined = abLens.get(a)
return b && bxLens.map(bxi => bxi.get(b))
},
(a: A, xs: X[]) => {
return abLens.set(a, {
...(abLens.get(a)),
...(xs.map((x, i) => bxLens[i].set({}, x)))
})
})
}
}
class CompositeLens<A, B, C> extends Lens<A, C> {
constructor(abLens: Lens<A, B>, bcLens: Lens<B, C>) {
super(
(a: A): C => {
const b = abLens.get(a)
return b && bcLens.get(b)
},
(a: A, c: C): A => {
const b0 = abLens.get(a) || {}
const b1 = bcLens.set(b0, c)
return abLens.set(a, b1)
})
}
}
class PropertyLens<A, B> extends Lens<A, B> {
constructor(propName: string) {
super(
(a: A) => a[propName],
(a: A, b: any) => {
const a1 = {...a}
a1[propName] = b
return a1
})
}
}
class LensApplicator<A> {
constructor(private obj: A) {
}
set(lens: Lens<A, any>, value: any) {
return new LensApplicator(lens.set(this.obj, value))
}
value() {
return this.obj
}
}
export function of<A, B>(prop: string) {
return new PropertyLens<A, B>(prop)
}
export function applicator<A>(obj: A) {
return new LensApplicator<A>(obj)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment