UNPKG

igogo

Version:

Fast Either and Maybe from Fantasy Land with Flow and TypeScript support

476 lines (358 loc) 10.5 kB
/* @flow */ import { getSet } from './getSet'; import { toIterator } from './toIterator'; import { toEmptyIterator } from './toEmptyIterator'; import * as maybe from './Maybe'; export interface Either<+L, +R> extends Iterable<R> { +isRight: boolean; +isLeft: boolean; map<R1>(transform: (R) => R1): Either<L, R1>; mapR<R1>(transform: (R) => R1): Either<L, R1>; mapL<L1>(transform: (L) => L1): Either<L1, R>; bimap<L1, R1>(transformL: (L) => L1, transformR: (R) => R1): Either<L1, R1>; mapTo<R1>(right: R1): Either<L, R1>; mapRTo<R1>(right: R1): Either<L, R1>; mapLTo<L1>(left: L1): Either<L1, R>; bimapTo<L1, R1>(left: L1, right: R1): Either<L1, R1>; chain<L1, R1>(transform: (R) => Either<L1, R1>): Either<L | L1, R1>; chainR<L1, R1>(transform: (R) => Either<L1, R1>): Either<L | L1, R1>; chainL<L1, R1>(transform: (L) => Either<L1, R1>): Either<L1, R | R1>; bichain<L1, R1>( transformL: (L) => Either<L1, R1>, transformR: (R) => Either<L1, R1>, ): Either<L1, R1>; ap<L1, R1>(either: Either<L1, (R) => R1>): Either<L | L1, R1>; apR<L1, R1>(either: Either<L1, (R) => R1>): Either<L | L1, R1>; apL<L1, R1>(either: Either<(L) => L1, R1>): Either<L1, R | R1>; biap<L1, R1>(either: Either<(L) => L1, (R) => R1>): Either<L1, R1>; alt<L1, R1>(either: Either<L1, R1>): Either<L1, R | R1>; or<L1, R1>(either: Either<L1, R1>): Either<L1, R | R1>; and<L1, R1>(either: Either<L1, R1>): Either<L | L1, R1>; getOr<R1>(value: R1): R | R1; getOrElse<R1>(fn: (L) => R1): R | R1; getLeftOr<L1>(value: L1): L | L1; getLeftOrElse<L1>(fn: () => L1): L | L1; getRightOr<R1>(value: R1): R | R1; getRightOrElse<R1>(fn: () => R1): R | R1; reduce<R1>(transform: (R1, R) => R1, or: R1): R1; reduceR<R1>(transform: (R1, R) => R1, or: R1): R1; reduceL<L1>(transform: (L1, L) => L1, or: L1): L1; tap(call: (R) => mixed): Either<L, R>; tapR(call: (R) => mixed): Either<L, R>; tapL(call: (L) => mixed): Either<L, R>; tapBoth(callL: (L) => mixed, callR: (R) => mixed): Either<L, R>; swap(): Either<R, L>; left(): maybe.Maybe<L>; right(): maybe.Maybe<R>; match<R1, L1>(fromRight: (R) => R1, fromLeft: (L) => L1): R1 | L1; toMaybe(): maybe.Maybe<R>; toMaybeR(): maybe.Maybe<R>; toMaybeL(): maybe.Maybe<L>; toMaybeLR(): maybe.Maybe<L | R>; promise(): Promise<R>; fold<T>(fromLeft: (L) => T, fromRight: (R) => T): T; } class EitherRight<+L, +R> implements Either<L, R> { constructor(value: R) { setRight(this, value); } get isRight() { return true; } get isLeft() { return false; } /* :: +@@iterator: () => Iterator<R>; */ map<R1>(transform: R => R1): Either<L, R1> { return this.mapR(transform); } mapR<R1>(transform: R => R1): Either<L, R1> { return new EitherRight(transform(getRight(this))); } mapL<L1>(): Either<L1, R> { return (this: Either<any, R>); } bimap<L1, R1>(transformL: L => L1, transformR: R => R1): Either<L1, R1> { return this.mapR(transformR).mapL(transformL); } mapTo<R1>(value: R1): Either<L, R1> { return Right(value); } mapRTo<R1>(value: R1): Either<L, R1> { return Right(value); } mapLTo<L1>(): Either<L1, R> { return (this: Either<any, R>); } bimapTo<L1, R1>(left: L1, right: R1): Either<L1, R1> { return this.mapRTo(right).mapLTo(left); } chain<L1, R1>(transform: R => Either<L1, R1>): Either<L | L1, R1> { return this.chainR(transform); } chainR<L1, R1>(transform: R => Either<L1, R1>): Either<L | L1, R1> { return transform(getRight(this)); } chainL<L1, R1>(): Either<L1, R | R1> { return (this: Either<any, R | R1>); } bichain<L1, R1>( transformL: L => Either<L1, R1>, transformR: R => Either<L1, R1>, ): Either<L1, R1> { return transformR(getRight(this)); } ap<L1, R1>(either: Either<L1, (R) => R1>): Either<L | L1, R1> { return this.apR(either); } apR<L1, R1>(either: Either<L1, (R) => R1>): Either<L | L1, R1> { return either.map(transform => transform(getRight(this))); } apL<L1, R1>(): Either<L1, R | R1> { return (this: Either<any, R | R1>); } biap<L1, R1>(either: Either<(L) => L1, (R) => R1>): Either<L1, R1> { return (this.apR(either): Either<any, R1>); } alt<L1, R1>(): Either<L1, R | R1> { return (this: Either<any, R | R1>); } or<L1, R1>(): Either<L1, R | R1> { return (this: Either<any, R | R1>); } and<L1, R1>(either: Either<L1, R1>): Either<L | L1, R1> { return either; } getOr(): R { return getRight(this); } getOrElse(): R { return getRight(this); } getLeftOr<L1>(value: L1): L1 { return value; } getLeftOrElse<L1>(fn: () => L1): L1 { return fn(); } getRightOr(): R { return getRight(this); } getRightOrElse(): R { return getRight(this); } reduce<R1>(transform: (R1, R) => R1, or: R1): R1 { return this.reduceR(transform, or); } reduceR<R1>(transform: (R1, R) => R1, or: R1): R1 { return transform(or, getRight(this)); } reduceL<L1>(transform: (L1, L) => L1, or: L1): L1 { return or; } tap(call: R => any): Either<L, R> { return this.tapR(call); } tapR(call: R => any): Either<L, R> { call(getRight(this)); return this; } tapL(): Either<L, R> { return this; } tapBoth(callL: L => mixed, callR: R => mixed): Either<L, R> { return this.tapR(callR); } swap(): Either<R, L> { return new EitherLeft(getRight(this)); } left(): maybe.Maybe<L> { return this.toMaybeL(); } right(): maybe.Maybe<R> { return this.toMaybeR(); } match<R1, L1>( fromRight: R => R1, /* :: fromLeft: L => L1, */ ): R1 | L1 { return fromRight(getRight(this)); } toMaybe(): maybe.Maybe<R> { return this.toMaybeR(); } toMaybeR(): maybe.Maybe<R> { return maybe.Just(getRight(this)); } toMaybeL(): maybe.Maybe<L> { return maybe.nothing; } toMaybeLR(): maybe.Maybe<L | R> { return this.toMaybe(); } promise(): Promise<R> { return Promise.resolve(getRight(this)); } fold<T>(fromLeft: L => T, fromRight: R => T): T { return fromRight(getRight(this)); } } class EitherLeft<+L, +R> implements Either<L, R> { constructor(value: L) { setLeft(this, value); } get isRight() { return false; } get isLeft() { return true; } /* :: +@@iterator: () => Iterator<R>; */ map<R1>(): Either<L, R1> { return (this: any); } mapR<R1>(): Either<L, R1> { return (this: any); } mapL<L1>(transform: L => L1): Either<L1, R> { return new EitherLeft(transform(getLeft(this))); } bimap<L1, R1>(transformL: L => L1, transformR: R => R1): Either<L1, R1> { return this.mapL(transformL).mapR(transformR); } mapTo<R1>(): Either<L, R1> { return (this: any); } mapRTo<R1>(): Either<L, R1> { return (this: any); } mapLTo<L1>(left: L1): Either<L1, R> { return new EitherLeft(left); } bimapTo<L1, R1>(left: L1, right: R1): Either<L1, R1> { return this.mapLTo(left).mapRTo(right); } mapBoth<L1, R1>(transformL: L => L1, transformR: R => R1): Either<L1, R1> { return this.bimap(transformL, transformR); } chain<L1, R1>(): Either<L | L1, R1> { return (this: any); } chainR<L1, R1>(): Either<L | L1, R1> { return (this: any); } chainL<L1, R1>(transform: L => Either<L1, R1>): Either<L1, R | R1> { return transform(getLeft(this)); } bichain<L1, R1>(transformL: L => Either<L1, R1>): Either<L1, R1> { return transformL(getLeft(this)); } ap<L1, R1>(): Either<L | L1, R1> { return (this: Either<L | L1, any>); } apR<L1, R1>(): Either<L | L1, R1> { return (this: Either<L | L1, any>); } apL<L1, R1>(either: Either<(L) => L1, R1>): Either<L1, R | R1> { return either.mapL(transform => transform(getLeft(this))); } biap<L1, R1>(either: Either<(L) => L1, (R) => R1>): Either<L1, R1> { return (this.apL(either): Either<L1, any>); } alt<L1, R1>(either: Either<L1, R1>): Either<L1, R | R1> { return either; } or<L1, R1>(either: Either<L1, R1>): Either<L1, R | R1> { return either; } and<L1, R1>(): Either<L | L1, R1> { return (this: Either<L | L1, any>); } getOr<R1>(value: R1): R1 { return value; } getOrElse<R1>(fn: L => R1): R1 { return fn(getLeft(this)); } getLeftOr(): L { return getLeft(this); } getLeftOrElse(): L { return getLeft(this); } getRightOr<R1>(value: R1): R1 { return value; } getRightOrElse<R1>(fn: () => R1): R1 { return fn(); } reduce<R1>(transform: (R1, R) => R1, or: R1): R1 { return or; } reduceR<R1>(transform: (R1, R) => R1, or: R1): R1 { return or; } reduceL<L1>(transform: (L1, L) => L1, or: L1): L1 { return transform(or, getLeft(this)); } tap(): Either<L, R> { return this; } tapR(): Either<L, R> { return this; } tapL(call: L => any): Either<L, R> { call(getLeft(this)); return this; } tapBoth(callL: L => mixed): Either<L, R> { return this.tapL(callL); } swap(): Either<R, L> { return new EitherRight(getLeft(this)); } left(): maybe.Maybe<L> { return this.toMaybeL(); } right(): maybe.Maybe<R> { return this.toMaybeR(); } match<R1, L1>(fromRight: R => R1, fromLeft: L => L1): R1 | L1 { return fromLeft(getLeft(this)); } toMaybe(): maybe.Maybe<R> { return this.toMaybeR(); } toMaybeR(): maybe.Maybe<R> { return maybe.nothing; } toMaybeL(): maybe.Maybe<L> { return maybe.Just(getLeft(this)); } toMaybeLR(): maybe.Maybe<L | R> { return this.toMaybeL(); } promise() { return Promise.reject(getLeft(this)); } fold<T>(fromLeft: L => T): T { return fromLeft(getLeft(this)); } } const [getRight, setRight]: [ <L, R>(EitherRight<L, R>) => R, <L, R>(EitherRight<L, R>, R) => R, ] = getSet('right'); const [getLeft, setLeft]: [ <L, R>(EitherLeft<L, R>) => L, <L, R>(EitherLeft<L, R>, L) => L, ] = getSet('left'); (EitherRight.prototype: any)[Symbol.iterator] = toIterator(getRight); (EitherLeft.prototype: any)[Symbol.iterator] = toEmptyIterator; export const Right = <L, R>(right: R): Either<L, R> => new EitherRight(right); export const Left = <L, R>(left: L): Either<L, R> => new EitherLeft(left); export const ifElse = <L, R>( condition: boolean, right: R, left: L, ): Either<L, R> => (condition ? Right(right) : Left(left));