Skip to content

Instantly share code, notes, and snippets.

@raveclassic
Created May 8, 2019 16:24
Show Gist options
  • Save raveclassic/5a6eccc789359e0f70ab5ed16cf7d216 to your computer and use it in GitHub Desktop.
Save raveclassic/5a6eccc789359e0f70ab5ed16cf7d216 to your computer and use it in GitHub Desktop.
Chaining effectful lenses
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