export type Left = {readonly _tag: 'Left', readonly left: E} export type Right = {readonly _tag: 'Right', readonly right: A} export type Either = Right | Left // Constructors export const left = (e: E): Either => ({ _tag: 'Left', left: e, }) export const right = (a: A): Either => ({ _tag: 'Right', right: a, }) // Operations export const eitherMap = (f: (a: A) => B) => (fa: Either): Either => fa._tag === 'Right' ? right(f(fa.right)) : fa export const eitherChain = (f: (a: A) => Either) => (fa: Either): Either => fa._tag === 'Right' ? f(fa.right) : fa export const fold = (onLeft: (e: E) => B, onRight: (a: A) => B) => (fa: Either): B => fa._tag === 'Left' ? onLeft(fa.left) : onRight(fa.right) // Do notation export class DoEither, E> { constructor(private readonly either: Either) {} bind( key: K, fa: Either, ): DoEither, E> { const newEither = eitherChain, E>((scope: T) => eitherMap, E>((a: A) => ({...scope, [key]: a}))(fa) )(this.either) return new DoEither(newEither) } return(f: (scope: T) => B): Either { return eitherMap(f)(this.either) } done(): Either { return this.either } do(fa: Either): DoEither { const newEither = eitherChain((scope: T) => eitherMap(() => scope)(fa) )(this.either) return new DoEither(newEither) } doL(f: (scope: T) => Either): DoEither { const newEither = eitherChain((scope: T) => eitherMap(() => scope)(f(scope)) )(this.either) return new DoEither(newEither) } static start(): DoEither<{}, E> { return new DoEither(right({})) } }