import { Monad } from '../monad/Monad'; /** * left wraps a value on the left side. */ export const left = (a: A) => new Left(a); /** * right wraps a value on the right side. */ export const right = (b: B) => new Right(b); /** * fromBoolean constructs an Either using a boolean value. */ export const fromBoolean = (b: boolean): Either => b ? right(true) : left(false); /** * Either monad implementation */ export abstract class Either implements Monad { static left = left; static right = right; static fromBoolean = fromBoolean; of(v: R): Either { return new Right(v); } abstract map(f: (r: R) => B): Either; abstract mapLeft(f: (l: L) => B): Either; abstract bimap(f: (l: L) => LL, g: (r: R) => RR): Either; /** * chain */ abstract chain(f: (r: R) => Either): Either; /** * orElse returns the result of f if the Either is left. */ abstract orElse(f: (l: L) => Either): Either; /** * orRight is like orElse except it just expects the value */ abstract orRight(f: (l: L) => R): Either; /** * ap */ abstract ap(e: Either B>): Either; /** * takeLeft extracts the left value of an Either, throwing an error if the Either is right. */ abstract takeLeft(): L; /** * takeRight is the opposite of left * @summary Either → B|Error */ abstract takeRight(): R; /** * cata */ abstract cata(f: (l: L) => B, g: (r: R) => B): B; } export class Left extends Either { constructor(public l: L) { super(); } map(_: (r: R) => B): Either { return new Left(this.l); } mapLeft(f: (l: L) => B): Either { return new Left(f(this.l)); } bimap(f: (l: L) => LL, _: (r: R) => RR): Either { return left(f(this.l)); } chain(_: (r: R) => Either): Either { return new Left(this.l); } orElse(f: (l: L) => Either): Either { return f(this.l); } orRight(f: (l: L) => R): Either { return new Right(f(this.l)); } ap(_: Either B>): Either { return new Left(this.l); } takeLeft(): L { return this.l; } takeRight(): R { throw new TypeError(`Not right!`); } cata(f: (l: L) => B, _: (r: R) => B): B { return f(this.l); } } export class Right extends Either { constructor(public r: R) { super(); } map(f: (r: R) => B): Either { return new Right(f(this.r)); } mapLeft(_: (l: L) => B): Either { return new Right(this.r); } bimap(_: (l: L) => LL, g: (r: R) => RR): Either { return right(g(this.r)); } chain(f: (r: R) => Either): Either { return f(this.r); } /** * orElse returns the result of f if the Either is left. */ orElse(_: (l: L) => Either): Either { return this; } orRight(_: (l: L) => R): Either { return this; } /** * ap */ ap(e: Either B>): Either { return e.map(f => f(this.r)); } /** * takeLeft extracts the left value of an Either, throwing an error if the Either is right. */ takeLeft(): L { throw new TypeError(`Not left!`); } takeRight(): R { return this.r; } /** * cata */ cata(_: (l: L) => B, g: (r: R) => B): B { return g(this.r); } }