Appearance
Either
ts
type Either<E, B> = Left<E> | Right<B>Either is a data type with two sides (Left and Right). It is most commonly used for error handling similar as Maybe.
It is also important to note that Either is right-biased which means that map, chain and other similar methods will operate on the right side.
Example
Without Either
ts
function parseJson(s: string): never | Record<string, unknown> {
try {
return JSON.parse(s)
} catch (e) {
throw new Error(`Couldn't parse from ${s}.`)
}
}
let parsed: Record<string, unknown>
// try to parse some content that may not be parsed
try {
parsed = parseJson('some content')
} catch(e) {
parsed = { message: 'err' }
}With Either
ts
import { tryCatch, getOrElse } from 'sa-lambda/either'
import { pipe } from 'sa-lambda'
const parseJson = (s: string) =>
tryCatch(() => JSON.parse(s), () => `Couldn't parse from ${s}.`)
let parsed = pipe('some content', parseJson, getOrElse(() => ({ message: 'err' })))API
left
ts
<E, A = never>(e: E) => Either<E, A>Constructs a new Either holding a Left value. Represents a failure value.
ts
left(1) ➔ { readonly _tag: 'Left', readonly left: 1 }right
ts
<A, E = never>(a: A) => Either<E, A>Constructs a new Either holding a Right value. Represents a successful value.
ts
right(1) ➔ { readonly _tag: 'Right', readonly right: 1 }isLeft
ts
<E>(ma: Either<E, unknown>) => ma is Left<E>Returns whether the Either is Left or not.
ts
isLeft(left(1)) ➔ true
isLeft(right(1)) ➔ falseisRight
ts
<A>(ma: Either<unknown, A>) => ma is Right<A>Returns whether the Either is Right or not.
ts
isRight(left(1)) ➔ false
isRight(right(1)) ➔ trueof
ts
<A, E = never>(a: A) => Either<E, A>Takes a value and wraps it into a Right.
ts
of(1) ➔ right(1)zero
ts
<E, A = never>(e: E) => Either<E, A>Alias of left
empty
ts
<E, A = never>(e: E) => Either<E, A>Alias of left
map
ts
<A, B>(f: (a: A) => B) => <E>(ma: Either<E, A>) => Left<E> | Right<B>Maps the Right value.
ts
pipe(left(1), map((n: number) => n + 1)) ➔ left(1)
pipe(right(1), map((n: number) => n + 1)) ➔ right(2)alt
ts
<E2, B>(that: Lazy<Either<E2, B>>) => <E1, A>(ma: Either<E1, A>) => Either<E2, B | A>Returns the Either if it is Right, otherwise returns the function result.
ts
pipe(right(2), alt(() => right(1))) ➔ right(2)
pipe(left(2), alt(() => right(1))) ➔ right(1)
pipe(right(2), alt(() => left(1))) ➔ right(2)
pipe(left(2), alt(() => left(1))) ➔ left(1)ap
ts
<E2, A>(ma: Either<E2, A>) => <E1, B>(fab: Either<E1, (a: A) => B>) => Either<E2 | E1, B>Applies a Right function over a Right value. Returns Left if the Either or the function is Left.
ts
pipe(left(1), ap(left(0))) ➔ left(1)
pipe(left(1), ap(right(0))) ➔ left(1)
pipe(right((n: number) => n + 1)), ap(left(0)) ➔ left(0)
pipe(right((n: number) => n + 1)), ap(right(0)) ➔ right(1)match
ts
<E, B, A, C>(onLeft: (e: E) => B, onRight: (a: A) => C) => (ma: Either<E, A>) => B | CTakes two functions and an Either value, if the value is Left, returns the onLeft function result, if the value is Right, returns the onRight function result.
ts
pipe(left(1), match((n: number) => n -1), (n: number) => n + 1) ➔ 0
pipe(right(1), match((n: number) => n -1), (n: number) => n + 1) ➔ 2extend
ts
<E, A, B>(f: (ma: Either<E, A>) => B) => (ma: Either<E, A>) => Left<E> | Right<B>Returns the Either if it's a Left, otherwise returns the result of the applying function and wrapped in a Right.
ts
pipe(right(1), extend(() => 2)) ➔ right(2)
pipe(left(1), extend(() => 2)) ➔ left(1)chain
ts
<E2, A, B>(f: (a: A) => Either<E2, B>) => <E1>(ma: Either<E1, A>) => Either<E2 | E1, B>Composes computations in sequence. Useful for chaining many computations that may fail.
ts
pipe(left(1), chain((n: number) => right(n + 1))) ➔ left(1)
pipe(right(1), chain((n: number) => right(n + 1))) ➔ right(2)chainRec
ts
<E, A, B>(f: (a: A) => Either<E, Either<A, B>>) => (ma: A) => Either<E, B>Chains recursively until the next is Right.
ts
pipe(right(1), chainRec( a => a < 5 ? left(a + 1) : right(`${a}`))) ➔ right('5')reduce
ts
<E, A, B>(f: (acc: B, a: A) => B, b: B) => (ma: Either<E, A>) => BTakes a function and an initial value and returns the initial value if Either is Left, otherwise returns the result of applying the function to the initial value and the value inside Either.
ts
pipe(right(1), reduce((acc, a) => acc + a, 1)) ➔ 2
pipe(left(0), reduce((acc, a) => acc + a, 1)) ➔ 1traverse
ts
PipeableTraverse2<EitherKind>Maps each element of a HKT structure to an action, and collects the results wrapped in Right.
Returns a HKT contains a left with the value of Either if the Either is a Left.
ts
const f = traverse(Maybe.Monad)((n: number) => n > 0 ? some(n): none
pipe(left('err'), f) ➔ some(left('err'))
pipe(right(1), f) ➔ some(right(1))
pipe(right(-1), f) ➔ noneswap
ts
<E, A>(ma: Either<E, A>) => Either<A, E>Returns Right if Either is Left and vice versa.
ts
swap(right(1)) ➔ left(1)
swap(left(1)) ➔ right(1)equals
ts
<E, A>(a: Either<E, A>, b: Either<E, A>) => booleanCompares one Either to another Either. Returns false if eithers or the wrapped values are different.
ts
equals(right(1), right(1)) ➔ true
equals(right(1), left(1)) ➔ false
equals(left(1), left(1)) ➔ true
equals(left(1), right(1)) ➔ falseexists
ts
<A>(predicate: Predicate<A>) => <E>(ma: Either<E, A>) => booleanReturns false if Either is a Left, otherwise returns the predicate result.
ts
pipe(left(0), exists((n: number) => n > 0)) ➔ false
pipe(right(0), exists((n: number) => n > 0)) ➔ false
pipe(left(1), exists((n: number) => n > 0)) ➔ false
pipe(right(1), exists((n: number) => n > 0)) ➔ truetryCatch
ts
<E, A>(f: Lazy<A>, onThrow: (e: unknown) => E) => Either<E, A>Returns a Either from a function that might throw.
ts
const unsafeDiv = (top: number, bottom: number) => {
if (bottom === 0) throw new Error('unsafe division')
return top / bottom
}
const div = (top: number, bottom: number) => tryCatch(() => unsafeDiv(top, bottom), () => 0)
div(2, 0) ➔ left(0)
div(2, 1) ➔ right(2)orElse
ts
<E1, E2, B>(onLeft: (e: E1) => Either<E2, B>) => <A>(ma: Either<E1, A>) => Either<E2, B | A>Returns Either if it's a Right, otherwise returns onLeft result.
ts
pipe(left(1), orElse((n: number) => right(n + 1))) ➔ right(2)
pipe(right(1), orElse((n: number) => right(n + 1))) ➔ right(1)extract
ts
<E, A>(ma: Either<E, A>) => E | AExtracts the value out of Either.
ts
extract(right(1)) ➔ 1
extract(left('err')) ➔ 'err'getLeft
ts
<E, A>(ma: Either<E, A>) => Maybe<E>Returns the Left value of an Either if possible.
ts
pipe(right(1), getLeft) ➔ none
pipe(left(1), getLeft) ➔ some(1)getRight
ts
<E, A>(ma: Either<E, A>) => Maybe<A>Returns the Right value of an Either if possible.
ts
pipe(right(1), getRight) ➔ some(1)
pipe(left(1), getRight) ➔ nonegetOrElse
ts
<E, B>(onLeft: (e: E) => B) => <A>(ma: Either<E, A>) => B | AReturns the Either value if it's a Right or a default onLeft result value if it's a Left.
ts
pipe(left(1), getOrElse(() => 0)) ➔ 0
pipe(right(1), getOrElse(() => 0)) ➔ 1filterOrElse
ts
<A, E2>(predicate: Predicate<A>, onFalse: (a: A) => E2) => <E1, B extends A>(ma: Either<E1, B>) => Left<E1> | Right<B> | Left<E2>Returns Either if it is a Left or the result of predicate is true, otherwise returns the result of applying onFalse function to value inside Either and wrapped in a Left.
ts
const f = filterOrElse(
(n: number) => n > 0,
() => 'err'
)
pipe(right(1), f) ➔ right(1)
pipe(right(-1), f) ➔ left('err')
pipe(left(1), f) ➔ left(1)fromPredicate
ts
<A, E>(predicate: Predicate<A>, onFalse: Lazy<E>): (a: A) => Either<E, A>Constructs a Left or Right based on the given predicate.
ts
pipe(1, fromPredicate((n: number) => n > 0, () => 'error')) ➔ right(1)
pipe(0, fromPredicate((n: number) => n > 0, () => 'error')) ➔ left('error')fromMaybe
ts
<E>(onNone: Lazy<E>) => <A>(ma: Maybe<A>) => Left<E> | Right<A>Returns Left or Right based on the given Maybe.
ts
pipe(some(1), fromMaybe(() => 'error')) ➔ right(1)
pipe(none, fromMaybe(() => 'error')) ➔ left('error')