Skip to content

Instantly share code, notes, and snippets.

@johnthecat
Last active September 10, 2023 20:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnthecat/2f860917969df63ad609f04a4116ffc4 to your computer and use it in GitHub Desktop.
Save johnthecat/2f860917969df63ad609f04a4116ffc4 to your computer and use it in GitHub Desktop.
Simplified functor-less implementation of Van Laarhoven lenses in Typescript.
export type Lens<S, A> = (f: (y: A) => A, x: S) => S;
export const view = <S, A>(lens: Lens<S, A>) => (x: S) => {
let value: unknown = null;
lens(x => (value = x), x);
// Because of lack of functor we can't create beautiful composition of .map.map... etc.,
// so Const functor here is just plain variable without initializer
return value as A;
};
export const set = <S, A>(lens: Lens<S, A>) => (x: S, v: A) => lens(() => v, x);
export const combineL = <A1, A2, A3>(lens1: Lens<A1, A2>, lens2: Lens<A2, A3>): Lens<A1, A3> => {
const viewLens1 = view(lens1);
const viewLens2 = view(lens2);
const setLens1 = set(lens1);
const setLens2 = set(lens2);
return (f, x) => {
const a = viewLens1(x);
return setLens1(x, setLens2(a, f(viewLens2(a))))
};
};
import { type Lens, view, set } from './lens.js';
type Address = {
street: string;
};
type Person = {
address: Address;
};
const personAddressL: Lens<Person, Address> = (f, x) => ({ ...x, address: f(x.address) });
const addressStreetL: Lens<Address, string> = (f, x) => ({ ...x, street: f(x.street) });
const personStreetL = combineL(personAddressL, addressStreetL);
const viewPersonAddress = view(personAddressL);
const setPersonAddress = set(personAddressL);
const testPerson: Person = {
address: { street: 'Ivory st.' },
};
expect(view(personAddressL)(testPerson)).toEqual({ street: 'Ivory st.' });
expect(view(personStreetL)(testPerson)).toEqual('Ivory st.');
expect(set(personAddressL)(testPerson, { street: 'Ivory st.' })).not.toBe(testPerson);
expect(set(personAddressL)(testPerson, { street: 'Sesame st.' })).toEqual({
...testPerson,
address: { street: 'Sesame st.' },
});
expect(set(personStreetL)(testPerson, 'Sesame st.')).toEqual({
...testPerson,
address: { street: 'Sesame st.' },
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment