Skip to content

Instantly share code, notes, and snippets.

@YBogomolov
Created April 13, 2020 15:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YBogomolov/24127fe7b287608a9ee7bb431f0d0bfc to your computer and use it in GitHub Desktop.
Save YBogomolov/24127fe7b287608a9ee7bb431f0d0bfc to your computer and use it in GitHub Desktop.
Encoding Either<E, A> as a tuple
export type Either<E, A> = [E, null] | [null, A];
type Fn<A, B> = (a: A) => B;
export const left = <E, A>(e: E): Either<E, A> => [e, null];
export const right = <E, A>(a: A): Either<E, A> => [null, a];
export const isLeft = <E, A>(e: Either<E, A>): e is [E, null] => e[1] === null;
export const isRight = <E, A>(e: Either<E, A>): e is [null, A] => e[0] === null;
/**
* Functor, bifunctor, monad, alternative, applicative instances, and `fold` to unwrap an Either:
*/
export const either = {
map: <E, A, B>(e: Either<E, A>, f: Fn<A, B>): Either<E, B> => (isRight(e) ? [null, f(e[1])] : e),
mapLeft: <E, E1, A>(e: Either<E, A>, f: Fn<E, E1>): Either<E1, A> => (isLeft(e) ? [f(e[0]), null] : e),
bimap: <E, E1, A, B>(e: Either<E, A>, f: Fn<E, E1>, g: Fn<A, B>): Either<E1, B> => (isRight(e) ? [null, g(e[1])] : [f(e[0]), null]),
of: right,
ap: <E, A, B>(e: Either<E, A>, ef: Either<E, Fn<A, B>>): Either<E, B> => either.chain(ef, f => either.map(e, f)),
chain: <E, A, B>(e: Either<E, A>, f: Fn<A, Either<E, B>>): Either<E, B> => (isRight(e) ? f(e[1]) : e),
alt: <E, A>(e: Either<E, A>, fallback: Either<E, A>): Either<E, A> => (isRight(e) ? e : fallback),
fold: <E, A, B>(e: Either<E, A>, onLeft: Fn<E, B>, onRight: Fn<A, B>): B => (isRight(e) ? onRight(e[1]) : onLeft(e[0]))
};
export const collectRight = <E, A>(es: Array<Either<E, A>>): Either<E, A[]> => es.reduce(
(acc, curr) => either.chain(curr, a => either.map(acc, as => as.concat(a))),
either.of<E, A[]>([])
);
export const collectAll = <E, A>(es: Array<Either<E, A>>): Either<E[], A[]> => es.reduce(
(acc, curr) => either.bimap(
acc,
lefts => (isLeft(curr) ? lefts.concat(curr[0]) : lefts),
rights => (isRight(curr) ? rights.concat(curr[1]) : rights)
),
either.of<E[], A[]>([])
);
@YBogomolov
Copy link
Author

I've also written a curried instances for sake of pointfree usage:

export const curried = {
    ...either,
    map: <A, B>(f: Fn<A, B>) => <E>(e: Either<E, A>): Either<E, B> => either.map(e, f),
    mapLeft: <E, E1>(f: Fn<E, E1>) => <A>(e: Either<E, A>): Either<E1, A> => either.mapLeft(e, f),
    bimap: <E, E1, A, B>(f: Fn<E, E1>, g: Fn<A, B>) => (e: Either<E, A>): Either<E1, B> => either.bimap(e, f, g),
    ap: <E, A, B>(ef: Either<E, Fn<A, B>>) => (e: Either<E, A>): Either<E, B> => either.ap(e, ef),
    chain: <E, A, B>(f: Fn<A, Either<E, B>>) => (e: Either<E, A>): Either<E, B> => either.chain(e, f),
    alt: <E, A>(fallback: Either<E, A>) => (e: Either<E, A>): Either<E, A> => either.alt(e, fallback),
    fold: <E, A, B>(onLeft: Fn<E, B>, onRight: Fn<A, B>) => (e: Either<E, A>): B => either.fold(e, onLeft, onRight)
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment