Skip to content

Instantly share code, notes, and snippets.

@monzee
Created April 25, 2020 05:36
Show Gist options
  • Save monzee/d97519f67736a6fd37cd2327c6ae2372 to your computer and use it in GitHub Desktop.
Save monzee/d97519f67736a6fd37cd2327c6ae2372 to your computer and use it in GitHub Desktop.
Church-encoded sum types in typescript
type Sum<K> = <T> (cases: Pattern<K, T>) => T;
type Pattern<K, T> = {
[C in keyof K]?: K[C] extends any[] ? (...args: K[C]) => T : never;
} & {
otherwise?: () => T
}
type List<T> = Sum<{nil: [], cons: [T, List<T>]}>;
function fallback<T>(): T {
throw new Error("unimplemented");
}
function empty<A>(): List<A> {
return ({nil = fallback}) => nil();
}
function append<A>(head: A, tail: List<A>): List<A> {
return ({cons = fallback}) => cons(head, tail);
}
function listOf<A>(...elems: A[]): List<A> {
let list = empty<A>();
for (let e of elems.reverse()) {
list = append(e, list);
}
return list;
}
function forEach<A>(list: List<A>, block: (a: A) => void) {
list({
nil() {},
cons(a, as) {
block(a);
forEach(as, block);
}
});
}
function map<A, B>(source: List<A>, transform: (a: A) => B): List<B> {
return ({nil: bNil = fallback, cons: bCons = fallback}) => source({
nil() {
return bNil();
},
cons(head, tail) {
return bCons(transform(head), map(tail, transform));
}
});
}
function reduce<A, B>(list: List<A>, zero: B, combine: (a: A, b: B) => B): B {
return list({
nil() {
return zero;
},
cons(a, as) {
return reduce(as, combine(a, zero), combine);
}
});
}
function count(list: List<any>): number {
return reduce(list, 0, (_, count) => count + 1);
}
function sum(numbers: List<number>): number {
return reduce(numbers, 0, (a, b) => a + b);
}
let ns = listOf(1, 2, 3, 4, 5);
let words = listOf(..."the quick brown fox jumps over the lazy dog".split(" "));
console.log(sum(ns));
console.log(count(words));
forEach(map(words, s => `${s}: ${s.length}`), console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment