Open cwmyers opened 10 years ago
As a scratch to start with:
interface Semigroup<T> { concat(a: T): T } class Writer<V, L extends Semigroup<L>> { public static of<X, Y extends Semigroup<Y>>(empty: Y) { return (value: X) => new Writer<X, Y>(value, empty) } constructor( private readonly value: V, private readonly log: L, ) { } public map<Z>(fn: (v: V) => Z): Writer<Z, L> { return new Writer(fn(this.value), this.log) } public flatMap<Z>(fn: (v: V) => Writer<Z, L>): Writer<Z, L> { const { value, log } = fn(this.value) return new Writer(value, this.log.concat(log)) } public toString() { return `(${this.value}, "${this.log}")` } public equals(other: Writer<V, L>) { return String(this) === String(other) } } const unit = Writer.of<number, string>('') const add5 = (a: number) => Writer.of<number, string>('added 5, ')(a + 5) const add8 = (a: number) => Writer.of<number, string>('added 8, ')(a + 8) const theOne = new Writer(1, 'Initial One, ') console.assert(unit(3).flatMap(add5).equals(add5(3)), 'Left identity') console.assert(theOne.flatMap(unit).equals(theOne), 'Right identity') console.assert(theOne.flatMap(add5).flatMap(add8).equals(theOne.flatMap(one => add5(one).flatMap(add8))) , 'Associativity')
As a scratch to start with: