Created
May 8, 2019 16:24
-
-
Save raveclassic/5a6eccc789359e0f70ab5ed16cf7d216 to your computer and use it in GitHub Desktop.
Chaining effectful lenses
This file contains 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
import { Lens } from 'monocle-ts'; | |
import { findFirst } from 'fp-ts/lib/Array'; | |
import { Predicate } from 'fp-ts/lib/function'; | |
import { none, option, Option, some } from 'fp-ts/lib/Option'; | |
import { Monad, Monad1, Monad2, Monad2C, Monad3, Monad3C } from 'fp-ts/lib/Monad'; | |
import { HKT, Type, Type2, Type3, URIS, URIS2, URIS3 } from 'fp-ts/lib/HKT'; | |
type Column = { | |
name: string; | |
filtering: Option<'ASC' | 'DESC'>; | |
}; | |
type DataTableConfig = { | |
columns: Column[]; | |
}; | |
const lensFromPredicate = <A>(p: Predicate<A>): Lens<A[], Option<A>> => | |
new Lens(as => findFirst(as, p), a => as => a.fold(as, a => as.map(a_ => (p(a_) ? a : a_)))); | |
const columns = Lens.fromProp<DataTableConfig>()('columns'); | |
const filteringLens = Lens.fromProp<Column>()('filtering'); | |
function chainLens<F extends URIS3>( | |
F: Monad3<F>, | |
): <U, L, A, B, C>(ab: Lens<A, Type3<F, U, L, B>>, bc: Lens<B, Type3<F, U, L, C>>) => Lens<A, Type3<F, U, L, C>>; | |
function chainLens<F extends URIS3, U, L>( | |
F: Monad3C<F, U, L>, | |
): <A, B, C>(ab: Lens<A, Type3<F, U, L, B>>, bc: Lens<B, Type3<F, U, L, C>>) => Lens<A, Type3<F, U, L, C>>; | |
function chainLens<F extends URIS2>( | |
F: Monad2<F>, | |
): <L, A, B, C>(ab: Lens<A, Type2<F, L, B>>, bc: Lens<B, Type2<F, L, C>>) => Lens<A, Type2<F, L, C>>; | |
function chainLens<F extends URIS2, L>( | |
F: Monad2C<F, L>, | |
): <A, B, C>(ab: Lens<A, Type2<F, L, B>>, bc: Lens<B, Type2<F, L, C>>) => Lens<A, Type2<F, L, C>>; | |
function chainLens<F extends URIS>( | |
F: Monad1<F>, | |
): <A, B, C>(ab: Lens<A, Type<F, B>>, bc: Lens<B, Type<F, C>>) => Lens<A, Type<F, C>>; | |
function chainLens<F>(F: Monad<F>): <A, B, C>(ab: Lens<A, HKT<F, B>>, bc: Lens<B, HKT<F, C>>) => Lens<A, HKT<F, C>>; | |
function chainLens<F>(F: Monad<F>): <A, B, C>(ab: Lens<A, HKT<F, B>>, bc: Lens<B, HKT<F, C>>) => Lens<A, HKT<F, C>> { | |
return (ab, bc) => new Lens(a => F.chain(ab.get(a), bc.get), oc => a => ab.set(F.map(ab.get(a), bc.set(oc)))(a)); | |
} | |
const chainOptionLens = chainLens(option); | |
const byName = (name: string) => | |
chainOptionLens(columns.composeLens(lensFromPredicate(column => column.name === name)), filteringLens); | |
describe('foo', () => { | |
it('foo', () => { | |
const config: DataTableConfig = { | |
columns: [ | |
{ | |
name: 'c1', | |
filtering: none, | |
}, | |
], | |
}; | |
const c1 = byName('c1'); | |
const c2 = byName('c2'); | |
expect(c1.set(none)(config)).toEqual(config); | |
expect(c1.set(some<'ASC'>('ASC'))(config)).toEqual({ | |
columns: [ | |
{ | |
name: 'c1', | |
filtering: some('ASC'), | |
}, | |
], | |
}); | |
expect(c2.set(none)(config)).toEqual(config); | |
expect(c2.set(some<'ASC'>('ASC'))(config)).toEqual(config); | |
}); | |
it('chainLens', () => { | |
type Bar = { | |
bar: Option<number>; | |
}; | |
type Foo = { | |
foo: Option<Bar>; | |
}; | |
const n = chainOptionLens(Lens.fromProp<Foo>()('foo'), Lens.fromProp<Bar>()('bar')); | |
expect(n.set(none)({ foo: none })).toEqual({ foo: none }); | |
expect(n.set(some(123))({ foo: none })).toEqual({ foo: none }); | |
expect(n.set(none)({ foo: some({ bar: none }) })).toEqual({ foo: some({ bar: none }) }); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment