/** * @since 1.0.0 */ import type { LazyArg } from "@fp-ts/core/Function" import { constNull, constUndefined, dual, identity } from "@fp-ts/core/Function" import type { Kind, TypeLambda } from "@fp-ts/core/HKT" import { structural } from "@fp-ts/core/internal/effect" import * as either from "@fp-ts/core/internal/Either" import * as option from "@fp-ts/core/internal/Option" import * as N from "@fp-ts/core/Number" import type { Option } from "@fp-ts/core/Option" import type { Predicate, Refinement } from "@fp-ts/core/Predicate" import * as applicative from "@fp-ts/core/typeclass/Applicative" import * as bicovariant from "@fp-ts/core/typeclass/Bicovariant" import * as chainable from "@fp-ts/core/typeclass/Chainable" import * as covariant from "@fp-ts/core/typeclass/Covariant" import type { Equivalence } from "@fp-ts/core/typeclass/Equivalence" import * as equivalence from "@fp-ts/core/typeclass/Equivalence" import * as flatMap_ from "@fp-ts/core/typeclass/FlatMap" import * as foldable from "@fp-ts/core/typeclass/Foldable" import * as invariant from "@fp-ts/core/typeclass/Invariant" import type * as monad from "@fp-ts/core/typeclass/Monad" import type { Monoid } from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import type * as pointed from "@fp-ts/core/typeclass/Pointed" import * as product_ from "@fp-ts/core/typeclass/Product" import type * as semiAlternative from "@fp-ts/core/typeclass/SemiAlternative" import * as semiApplicative from "@fp-ts/core/typeclass/SemiApplicative" import * as semiCoproduct from "@fp-ts/core/typeclass/SemiCoproduct" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" // ------------------------------------------------------------------------------------- // models // ------------------------------------------------------------------------------------- /** * @category models * @since 1.0.0 */ export interface Left { readonly _tag: "Left" readonly left: E } /** * @category models * @since 1.0.0 */ export interface Right { readonly _tag: "Right" readonly right: A } /** * @category models * @since 1.0.0 */ export type Either = Left | Right /** * @category type lambdas * @since 1.0.0 */ export interface EitherTypeLambda extends TypeLambda { readonly type: Either } // ------------------------------------------------------------------------------------- // constructors // ------------------------------------------------------------------------------------- /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure. * * @category constructors * @since 1.0.0 */ export const right: (a: A) => Either = either.right /** * Constructs a new `Either` holding a `Left` value. This usually represents a failure, due to the right-bias of this * structure. * * @category constructors * @since 1.0.0 */ export const left: (e: E) => Either = either.left /** * Alias of {@link right}. * * @category constructors * @since 1.0.0 */ export const of: (a: A) => Either = right // ------------------------------------------------------------------------------------- // guards // ------------------------------------------------------------------------------------- /** * Tests if a value is a `Either`. * * @param input - The value to test. * * @example * import { isEither, left, right } from '@fp-ts/core/Either' * * assert.deepStrictEqual(isEither(right(1)), true) * assert.deepStrictEqual(isEither(left("error")), true) * assert.deepStrictEqual(isEither({ right: 1 }), false) * * @category guards * @since 1.0.0 */ export const isEither = (input: unknown): input is Either => typeof input === "object" && input != null && structural in input && "_tag" in input && (input["_tag"] === "Left" || input["_tag"] === "Right") /** * Determine if a `Either` is a `Left`. * * @param self - The `Either` to check. * * @example * import { isLeft, left, right } from '@fp-ts/core/Either' * * assert.deepStrictEqual(isLeft(right(1)), false) * assert.deepStrictEqual(isLeft(left("error")), true) * * @category guards * @since 1.0.0 */ export const isLeft: (self: Either) => self is Left = either.isLeft /** * Determine if a `Either` is a `Right`. * * @param self - The `Either` to check. * * @example * import { isRight, left, right } from '@fp-ts/core/Either' * * assert.deepStrictEqual(isRight(right(1)), true) * assert.deepStrictEqual(isRight(left("error")), false) * * @category guards * @since 1.0.0 */ export const isRight: (self: Either) => self is Right = either.isRight // ------------------------------------------------------------------------------------- // conversions // ------------------------------------------------------------------------------------- /** * Returns a `Refinement` from a `Either` returning function. * This function ensures that a `Refinement` definition is type-safe. * * @category conversions * @since 1.0.0 */ export const toRefinement = (f: (a: A) => Either): Refinement => (a: A): a is B => isRight(f(a)) /** * @category conversions * @since 1.0.0 */ export const fromIterable: { (onEmpty: LazyArg): (collection: Iterable) => Either (collection: Iterable, onEmpty: LazyArg): Either } = dual(2, (collection: Iterable, onEmpty: LazyArg): Either => { for (const a of collection) { return right(a) } return left(onEmpty()) }) /** * Converts a `Either` to an `Option` discarding the error. * * @param self - The `Either` to convert to an `Option`. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(E.toOption(E.right(1)), O.some(1)) * assert.deepStrictEqual(E.toOption(E.left('a')), O.none()) * * @category conversions * @since 1.0.0 */ export const toOption: (self: Either) => Option = either.getRight /** * Converts a `Either` to an `Option` discarding the error. * * Alias of {@link toOption}. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(E.getRight(E.right('ok')), O.some('ok')) * assert.deepStrictEqual(E.getRight(E.left('err')), O.none()) * * @category conversions * @since 1.0.0 */ export const getRight: (self: Either) => Option = toOption /** * Converts a `Either` to an `Option` discarding the value. * * @example * import * as O from '@fp-ts/core/Option' * import * as E from '@fp-ts/core/Either' * * assert.deepStrictEqual(E.getLeft(E.right('ok')), O.none()) * assert.deepStrictEqual(E.getLeft(E.left('err')), O.some('err')) * * @category conversions * @since 1.0.0 */ export const getLeft: (self: Either) => Option = either.getLeft /** * @example * import * as E from '@fp-ts/core/Either' * import * as O from '@fp-ts/core/Option' * * assert.deepStrictEqual(E.fromOption(O.some(1), () => 'error'), E.right(1)) * assert.deepStrictEqual(E.fromOption(O.none(), () => 'error'), E.left('error')) * * @category conversions * @since 1.0.0 */ export const fromOption: { (fa: Option, onNone: () => E): Either (onNone: () => E): (fa: Option) => Either } = either.fromOption // ------------------------------------------------------------------------------------- // equivalence // ------------------------------------------------------------------------------------- /** * @category equivalence * @since 1.0.0 */ export const getEquivalence = ( EE: Equivalence, EA: Equivalence ): Equivalence> => equivalence.make((x, y) => x === y || (isLeft(x) ? isLeft(y) && EE(x.left, y.left) : isRight(y) && EA(x.right, y.right)) ) /** * @category mapping * @since 1.0.0 */ export const bimap: { (f: (e: E1) => E2, g: (a: A) => B): (self: Either) => Either (self: Either, f: (e: E1) => E2, g: (a: A) => B): Either } = dual( 3, (self: Either, f: (e: E1) => E2, g: (a: A) => B): Either => isLeft(self) ? left(f(self.left)) : right(g(self.right)) ) /** * @category instances * @since 1.0.0 */ export const Bicovariant: bicovariant.Bicovariant = { bimap } /** * Maps the `Left` side of an `Either` value to a new `Either` value. * * @param self - The input `Either` value to map. * @param f - A transformation function to apply to the `Left` value of the input `Either`. * * @category error handling * @since 1.0.0 */ export const mapLeft: { (f: (e: E) => G): (self: Either) => Either (self: Either, f: (e: E) => G): Either } = bicovariant.mapLeft(Bicovariant) /** * Maps the `Right` side of an `Either` value to a new `Either` value. * * @param self - An `Either` to map * @param f - The function to map over the value of the `Either` * * @category mapping * @since 1.0.0 */ export const map: { (f: (a: A) => B): (self: Either) => Either (self: Either, f: (a: A) => B): Either } = dual( 2, (self: Either, f: (a: A) => B): Either => isRight(self) ? right(f(self.right)) : self ) const imap = covariant.imap(map) /** * @category instances * @since 1.0.0 */ export const Covariant: covariant.Covariant = { imap, map } /** * @category instances * @since 1.0.0 */ export const Invariant: invariant.Invariant = { imap } /** * @category mapping * @since 1.0.0 */ export const tupled: (self: Either) => Either = invariant.tupled( Invariant ) /** * @category mapping * @since 1.0.0 */ export const flap: { (a: A, self: Either B>): Either (self: Either B>): (a: A) => Either } = covariant.flap(Covariant) /** * Maps the Right value of this effect to the specified constant value. * * @category mapping * @since 1.0.0 */ export const as: { (self: Either, b: B): Either (b: B): (self: Either) => Either } = covariant.as(Covariant) /** * Returns the effect Eithering from mapping the Right of this effect to unit. * * @category mapping * @since 1.0.0 */ export const asUnit: (self: Either) => Either = covariant.asUnit( Covariant ) /** * @category instances * @since 1.0.0 */ export const Of: of_.Of = { of } /** * @since 1.0.0 */ export const unit: Either = of_.unit(Of) /** * @category instances * @since 1.0.0 */ export const Pointed: pointed.Pointed = { of, imap, map } /** * @category sequencing * @since 1.0.0 */ export const flatMap: { (f: (a: A) => Either): (self: Either) => Either (self: Either, f: (a: A) => Either): Either } = dual( 2, (self: Either, f: (a: A) => Either): Either => isLeft(self) ? self : f(self.right) ) /** * @category instances * @since 1.0.0 */ export const FlatMap: flatMap_.FlatMap = { flatMap } /** * @since 1.0.0 */ export const flatten: (self: Either>) => Either = flatMap_ .flatten(FlatMap) /** * @since 1.0.0 */ export const andThen: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = flatMap_.andThen(FlatMap) /** * @since 1.0.0 */ export const composeKleisliArrow: { ( afb: (a: A) => Either, bfc: (b: B) => Either ): (a: A) => Either ( bfc: (b: B) => Either ): (afb: (a: A) => Either) => (a: A) => Either } = flatMap_.composeKleisliArrow(FlatMap) /** * @category instances * @since 1.0.0 */ export const Chainable: chainable.Chainable = { imap, map, flatMap } /** * Sequences the specified effect after this effect, but ignores the value * produced by the effect. * * @category sequencing * @since 1.0.0 */ export const andThenDiscard: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = chainable.andThenDiscard(Chainable) /** * @category instances * @since 1.0.0 */ export const Monad: monad.Monad = { imap, of, map, flatMap } const product = (self: Either, that: Either): Either => isRight(self) ? (isRight(that) ? right([self.right, that.right]) : that) : self const productMany = ( self: Either, collection: Iterable> ): Either]> => { if (isLeft(self)) { return self } const out: [A, ...Array] = [self.right] for (const e of collection) { if (isLeft(e)) { return e } out.push(e.right) } return right(out) } /** * @category instances * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { imap, product, productMany } /** * Appends an element to the end of a tuple. * * @since 1.0.0 */ export const appendElement: { , E2, B>( self: Either, that: Either ): Either ( that: Either ): >(self: Either) => Either } = semiProduct.appendElement(SemiProduct) const productAll = ( collection: Iterable> ): Either> => { const out: Array = [] for (const e of collection) { if (isLeft(e)) { return e } out.push(e.right) } return right(out) } /** * @category instances * @since 1.0.0 */ export const Product: product_.Product = { of, imap, product, productMany, productAll } /** * @since 1.0.0 */ export const tuple: >>( ...tuple: T ) => Either< [T[number]] extends [Either] ? E : never, { [I in keyof T]: [T[I]] extends [Either] ? A : never } > = product_.tuple(Product) /** * @since 1.0.0 */ export const struct: >>( r: R ) => Either< [R[keyof R]] extends [Either] ? E : never, { [K in keyof R]: [R[K]] extends [Either] ? A : never } > = product_.struct(Product) /** * @category instances * @since 1.0.0 */ export const SemiApplicative: semiApplicative.SemiApplicative = { imap, map, product, productMany } /** * Lifts a binary function into `Either`. * * @param f - The function to lift. * * @category lifting * @since 1.0.0 */ export const lift2: (f: (a: A, b: B) => C) => { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = semiApplicative.lift2(SemiApplicative) /** * @category combining * @since 1.0.0 */ export const zipWith: { ( self: Either, that: Either, f: (a: A, b: B) => C ): Either ( that: Either, f: (a: A, b: B) => C ): (self: Either) => Either } = semiApplicative.zipWith(SemiApplicative) /** * @since 1.0.0 */ export const ap: { (self: Either B>, that: Either): Either (that: Either): (self: Either B>) => Either } = semiApplicative.ap(SemiApplicative) /** * @category instances * @since 1.0.0 */ export const Applicative: applicative.Applicative = { imap, of, map, product, productMany, productAll } /** * `Semigroup` returning the left-most `Left` value. If both operands are `Right`s then the inner values * are combined using the provided `Semigroup`. * * ``` * | self | that | combine(self, that) | * | ---------- | ---------- | ----------------------- | * | left(e1) | left(e2) | left(e1) | * | left(e1) | right(a2) | left(e1) | * | right(a1) | left(e2) | left(e2) | * | right(a1) | right(a2) | right(combine(a1, a2)) | * ``` * * @category combining * @since 1.0.0 */ export const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> = semiApplicative.getSemigroup(SemiApplicative) /** * `Monoid` returning the left-most `Left` value. If both operands are `Right`s then the inner values * are combined using the provided `Monoid`. * * - `combine` is provided by {@link getFirstLeftSemigroup}. * - `empty` is `right(M.empty)` * * @category combining * @since 1.0.0 */ export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative .getMonoid(Applicative) const coproduct = ( self: Either, that: Either ): Either => isRight(self) ? self : that const coproductMany = ( self: Either, collection: Iterable> ): Either => { let out = self if (isRight(out)) { return out } for (out of collection) { if (isRight(out)) { return out } } return out } /** * @category instances * @since 1.0.0 */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { imap, coproduct, coproductMany } /** * @category error handling * @since 1.0.0 */ export const firstRightOf: { (collection: Iterable>): (self: Either) => Either (self: Either, collection: Iterable>): Either } = dual(2, coproductMany) /** * Semigroup returning the left-most `Right` value. * * ``` * | self | that | combine(self, that) | * | ---------- | ---------- | ------------------- | * | left(e1) | left(e2) | left(e2) | * | left(e1) | right(a2) | right(a2) | * | right(a1) | left(e2) | right(a1) | * | right(a1) | right(a2) | right(a1) | * ``` * * @category combining * @since 1.0.0 */ export const getFirstRightSemigroup: () => Semigroup> = semiCoproduct .getSemigroup(SemiCoproduct) /** * Returns the wrapped value if it's a `Right` or a default value if is a `Left`. * * @example * import * as E from '@fp-ts/core/Either' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual( * E.getOrElse(E.right(1), () => 0), * 1 * ) * assert.deepStrictEqual( * E.getOrElse(E.left('error'), () => 0), * 0 * ) * * @category getters * @since 1.0.0 */ export const getOrElse: { (onLeft: (e: E) => B): (self: Either) => B | A (self: Either, onLeft: (e: E) => B): A | B } = dual( 2, (self: Either, onLeft: (e: E) => B): A | B => isLeft(self) ? onLeft(self.left) : self.right ) /** * Executes this effect and returns its value, if it succeeds, but otherwise * executes the specified effect. * * @category error handling * @since 1.0.0 */ export const orElse: { (that: (e1: E1) => Either): (self: Either) => Either (self: Either, that: (e1: E1) => Either): Either } = dual( 2, (self: Either, that: (e1: E1) => Either): Either => isLeft(self) ? that(self.left) : self ) /** * Returns an effect that will produce the value of this effect, unless it * fails, in which case, it will produce the value of the specified effect. * * @category error handling * @since 1.0.0 */ export const orElseEither: { (that: (e1: E1) => Either): (self: Either) => Either> (self: Either, that: (e1: E1) => Either): Either> } = dual( 2, (self: Either, that: (e1: E1) => Either): Either> => isLeft(self) ? map(that(self.left), right) : map(self, left) ) /** * Executes this effect and returns its value, if it succeeds, but otherwise * fails with the specified error. * * @category error handling * @since 1.0.0 */ export const orElseFail: { (onLeft: LazyArg): (self: Either) => Either (self: Either, onLeft: LazyArg): Either } = dual( 2, (self: Either, onLeft: LazyArg): Either => orElse(self, () => left(onLeft())) ) /** * @category instances * @since 1.0.0 */ export const SemiAlternative: semiAlternative.SemiAlternative = { map, imap, coproduct, coproductMany: firstRightOf } /** * @category instances * @since 1.0.0 */ export const Foldable: foldable.Foldable = { reduce: dual( 3, (self: Either, b: B, f: (b: B, a: A) => B): B => isLeft(self) ? b : f(b, self.right) ) } /** * Transforms an `Either` into an `Array`. * If the input is `Left`, an empty array is returned. * If the input is `Right`, the value is wrapped in an array. * * @param self - The `Either` to convert to an array. * * @example * import { right, left, toArray } from '@fp-ts/core/Either' * * assert.deepStrictEqual(toArray(right(1)), [1]) * assert.deepStrictEqual(toArray(left("error")), []) * * @category conversions * @since 1.0.0 */ export const toArray: (self: Either) => Array = foldable.toArray(Foldable) /** * Takes two functions and an `Either` value, if the value is a `Left` the inner value is applied to the first function, * if the value is a `Right` the inner value is applied to the second function. * * @example * import * as E from '@fp-ts/core/Either' * import { pipe } from '@fp-ts/core/Function' * * const onLeft = (errors: ReadonlyArray): string => `Errors: ${errors.join(', ')}` * * const onRight = (value: number): string => `Ok: ${value}` * * assert.deepStrictEqual( * pipe( * E.right(1), * E.match(onLeft , onRight) * ), * 'Ok: 1' * ) * assert.deepStrictEqual( * pipe( * E.left(['error 1', 'error 2']), * E.match(onLeft , onRight) * ), * 'Errors: error 1, error 2' * ) * * @category pattern matching * @since 1.0.0 */ export const match: { (onLeft: (e: E) => B, onRight: (a: A) => C): (self: Either) => B | C (self: Either, onLeft: (e: E) => B, onRight: (a: A) => C): B | C } = dual( 3, (self: Either, onLeft: (e: E) => B, onRight: (a: A) => C): B | C => isLeft(self) ? onLeft(self.left) : onRight(self.right) ) // ------------------------------------------------------------------------------------- // interop // ------------------------------------------------------------------------------------- /** * Takes a lazy default and a nullable value, if the value is not nully, turn it into a `Right`, if the value is nully use * the provided default as a `Left`. * * @example * import * as E from '@fp-ts/core/Either' * * const parse = E.fromNullable(() => 'nullable') * * assert.deepStrictEqual(parse(1), E.right(1)) * assert.deepStrictEqual(parse(null), E.left('nullable')) * * @category interop * @since 1.0.0 */ export const fromNullable: { (onNullable: (a: A) => E): (a: A) => Either> (a: A, onNullable: (a: A) => E): Either> } = dual( 2, (a: A, onNullable: (a: A) => E): Either> => a == null ? left(onNullable(a)) : right(a as NonNullable) ) /** * @category interop * @since 1.0.0 */ export const liftNullable = , B, E>( f: (...a: A) => B | null | undefined, onNullable: (...a: A) => E ) => (...a: A): Either> => fromNullable(f(...a), () => onNullable(...a)) /** * @category interop * @since 1.0.0 */ export const merge: (self: Either) => E | A = match(identity, identity) /** * @category sequencing * @since 1.0.0 */ export const flatMapNullable: { ( f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): (self: Either) => Either> ( self: Either, f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): Either> } = dual(3, ( self: Either, f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): Either> => flatMap(self, liftNullable(f, onNullable))) /** * Extracts the value of an `Either` or throws if the `Either` is `Left`. * * If a default error is sufficient for your use case and you don't need to configure the thrown error, see {@link getOrThrow}. * * @param self - The `Either` to extract the value from. * @param onLeft - A function that will be called if the `Either` is `Left`. It returns the error to be thrown. * * @example * import * as E from "@fp-ts/core/Either" * * assert.deepStrictEqual( * E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), * 1 * ) * assert.throws(() => E.getOrThrowWith(E.left("error"), () => new Error('Unexpected Left'))) * * @category interop * @since 1.0.0 */ export const getOrThrowWith: { (onLeft: (e: E) => unknown): (self: Either) => A (self: Either, onLeft: (e: E) => unknown): A } = dual(2, (self: Either, onLeft: (e: E) => unknown): A => { if (isRight(self)) { return self.right } throw onLeft(self.left) }) /** * Extracts the value of an `Either` or throws if the `Either` is `Left`. * * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. * * @param self - The `Either` to extract the value from. * @throws `Error("getOrThrow called on a Left")` * * @example * import * as E from "@fp-ts/core/Either" * * assert.deepStrictEqual(E.getOrThrow(E.right(1)), 1) * assert.throws(() => E.getOrThrow(E.left("error"))) * * @category interop * @since 1.0.0 */ export const getOrThrow: (self: Either) => A = getOrThrowWith(() => new Error("getOrThrow called on a Left") ) /** * Lifts a function that may throw to one returning a `Either`. * * @category interop * @since 1.0.0 */ export const liftThrowable = , B, E>( f: (...a: A) => B, onThrow: (error: unknown) => E ): ((...a: A) => Either) => (...a) => { try { return right(f(...a)) } catch (e) { return left(onThrow(e)) } } /** * @since 1.0.0 */ export const reverse = (self: Either): Either => isLeft(self) ? right(self.left) : left(self.right) /** * @category filtering * @since 1.0.0 */ export const filter: { (refinement: Refinement, onFalse: LazyArg): ( self: Either ) => Either ( predicate: Predicate, onFalse: LazyArg ): (self: Either) => Either ( self: Either, refinement: Refinement, onFalse: LazyArg ): Either ( self: Either, predicate: Predicate, onFalse: LazyArg ): Either } = dual(3, ( self: Either, predicate: Predicate, onFalse: LazyArg ): Either => isLeft(self) ? self : predicate(self.right) ? self : left(onFalse())) /** * @category filtering * @since 1.0.0 */ export const filterMap: { ( f: (a: A) => Option, onNone: LazyArg ): (self: Either) => Either ( self: Either, f: (a: A) => Option, onNone: LazyArg ): Either } = dual(3, ( self: Either, f: (a: A) => Option, onNone: LazyArg ): Either => flatMap(self, (a) => { const ob = f(a) return option.isNone(ob) ? left(onNone()) : right(ob.value) })) /** * @category filtering * @since 1.0.0 */ export const compact: { (onNone: LazyArg): (self: Either>) => Either (self: Either>, onNone: LazyArg): Either } = dual( 2, (self: Either>, onNone: LazyArg): Either => filterMap(self, identity, onNone) ) /** * @category traversing * @since 1.0.0 */ export const traverse = ( F: applicative.Applicative ): { ( f: (a: A) => Kind ): (self: Either) => Kind> ( self: Either, f: (a: A) => Kind ): Kind> } => dual(2, ( self: Either, f: (a: A) => Kind ): Kind> => isLeft(self) ? F.of>(self) : F.map>(f(self.right), right)) /** * @category instances * @since 1.0.0 */ export const Traversable: traversable.Traversable = { traverse } /** * @category traversing * @since 1.0.0 */ export const sequence: ( F: applicative.Applicative ) => ( self: Either> ) => Kind> = traversable.sequence(Traversable) /** * @category traversing * @since 1.0.0 */ export const traverseTap: ( F: applicative.Applicative ) => { ( self: Either, f: (a: A) => Kind ): Kind> ( f: (a: A) => Kind ): (self: Either) => Kind> } = traversable.traverseTap(Traversable) /** * Returns an effect that effectfully "peeks" at the success of this effect. * * @category combinators * @since 1.0.0 */ export const tap: { (self: Either, f: (a: A) => Either): Either (f: (a: A) => Either): (self: Either) => Either } = chainable.tap(Chainable) // ------------------------------------------------------------------------------------- // debugging // ------------------------------------------------------------------------------------- /** * @category debugging * @since 1.0.0 */ export const inspectRight: { (onRight: (a: A) => void): (self: Either) => Either (self: Either, onRight: (a: A) => void): Either } = dual(2, (self: Either, onRight: (a: A) => void): Either => { if (isRight(self)) { onRight(self.right) } return self }) /** * @category debugging * @since 1.0.0 */ export const inspectLeft: { (onLeft: (e: E) => void): (self: Either) => Either (self: Either, onLeft: (e: E) => void): Either } = dual(2, (self: Either, onLeft: (e: E) => void): Either => { if (isLeft(self)) { onLeft(self.left) } return self }) /** * Returns an effect that effectfully "peeks" at the failure of this effect. * * @category error handling * @since 1.0.0 */ export const tapError: { (onLeft: (e: E1) => Either): (self: Either) => Either (self: Either, onLeft: (e: E1) => Either): Either } = dual( 2, (self: Either, onLeft: (e: E1) => Either): Either => { if (isRight(self)) { return self } const out = onLeft(self.left) return isLeft(out) ? out : self } ) /** * @category getters * @since 1.0.0 */ export const getOrNull: (self: Either) => A | null = getOrElse(constNull) /** * @category getters * @since 1.0.0 */ export const getOrUndefined: (self: Either) => A | undefined = getOrElse(constUndefined) /** * @example * import { liftPredicate, left, right } from '@fp-ts/core/Either' * import { pipe } from '@fp-ts/core/Function' * * assert.deepStrictEqual( * pipe( * 1, * liftPredicate((n) => n > 0, () => 'error') * ), * right(1) * ) * assert.deepStrictEqual( * pipe( * -1, * liftPredicate((n) => n > 0, () => 'error') * ), * left('error') * ) * * @category lifting * @since 1.0.0 */ export const liftPredicate: { ( refinement: Refinement, onFalse: (c: C) => E ): (c: C) => Either (predicate: Predicate, onFalse: (b: B) => E): (b: B) => Either } = (predicate: Predicate, onFalse: (b: B) => E) => (b: B) => predicate(b) ? right(b) : left(onFalse(b)) /** * @category lifting * @since 1.0.0 */ export const liftOption = , B, E>( f: (...a: A) => Option, onNone: (...a: A) => E ) => (...a: A): Either => fromOption(() => onNone(...a))(f(...a)) /** * @category sequencing * @since 1.0.0 */ export const flatMapOption: { ( f: (a: A) => Option, onNone: (a: A) => E2 ): (self: Either) => Either ( self: Either, f: (a: A) => Option, onNone: (a: A) => E2 ): Either } = dual(3, ( self: Either, f: (a: A) => Option, onNone: (a: A) => E2 ): Either => flatMap(self, liftOption(f, onNone))) /** * Returns a function that checks if an `Either` contains a given value using a provided `equivalence` function. * * @since 1.0.0 */ export const contains = (isEquivalent: (self: A, that: A) => boolean): { (a: A): (self: Either) => boolean (self: Either, a: A): boolean } => dual( 2, (self: Either, a: A): boolean => isLeft(self) ? false : isEquivalent(self.right, a) ) /** * Returns `false` if `Left` or returns the Either of the application of the given predicate to the `Right` value. * * @example * import * as E from '@fp-ts/core/Either' * * const f = E.exists((n: number) => n > 2) * * assert.deepStrictEqual(f(E.left('a')), false) * assert.deepStrictEqual(f(E.right(1)), false) * assert.deepStrictEqual(f(E.right(3)), true) * * @since 1.0.0 */ export const exists: { (predicate: Predicate): (self: Either) => boolean (self: Either, predicate: Predicate): boolean } = dual( 2, (self: Either, predicate: Predicate): boolean => isLeft(self) ? false : predicate(self.right) ) /** * Semigroup that models the combination of values that may be absent, elements that are `Left` are ignored * while elements that are `Right` are combined using the provided `Semigroup`. * * @category instances * @since 1.0.0 */ export const getOptionalSemigroup = (S: Semigroup): Semigroup> => semigroup.make(( x, y ) => (isLeft(y) ? x : isLeft(x) ? y : right(S.combine(x.right, y.right)))) // ------------------------------------------------------------------------------------- // algebraic operations // ------------------------------------------------------------------------------------- /** * @category algebraic operations * @since 1.0.0 */ export const sum: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = lift2(N.sum) /** * @category algebraic operations * @since 1.0.0 */ export const multiply: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = lift2(N.multiply) /** * @category algebraic operations * @since 1.0.0 */ export const subtract: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = lift2(N.subtract) /** * @category algebraic operations * @since 1.0.0 */ export const divide: { (self: Either, that: Either): Either (that: Either): (self: Either) => Either } = lift2(N.divide) // ------------------------------------------------------------------------------------- // utils // ------------------------------------------------------------------------------------- /** * Return all the `Right` elements from an `Interable` of `Either`s. * * @category getters * @since 1.0.0 */ export const rights = (self: Iterable>): Array => { const out: Array = [] for (const a of self) { if (isRight(a)) { out.push(a.right) } } return out } /** * Return all the `Left` elements from an `Interable` of `Either`s. * * @category getters * @since 1.0.0 */ export const lefts = (self: Iterable>): Array => { const out: Array = [] for (const a of self) { if (isLeft(a)) { out.push(a.left) } } return out } // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- /** * @category do notation * @since 1.0.0 */ export const bindTo: { ( name: N ): (self: Either) => Either (self: Either, name: N): Either } = invariant.bindTo(Invariant) const let_: { ( name: Exclude, f: (a: A) => B ): ( self: Either ) => Either ( self: Either, name: Exclude, f: (a: A) => B ): Either } = covariant.let(Covariant) export { /** * @category do notation * @since 1.0.0 */ let_ as let } /** * @category do notation * @since 1.0.0 */ export const Do: Either = of_.Do(Of) /** * @category do notation * @since 1.0.0 */ export const bind: { ( name: Exclude, f: (a: A) => Either ): ( self: Either ) => Either ( self: Either, name: Exclude, f: (a: A) => Either ): Either } = chainable.bind(Chainable) /** * Extends the `Either` value with the value of another `Either` type. * * If both `Either` instances are `Left`, then the result will be the first `Left`. * * @param self - The original `Either` value. * @param name - The name of the property that will be added to the original `Either` type. * @param that - The `Either` value that will be added to the original `Either` type. * * @example * import * as E from '@fp-ts/core/Either' * import { pipe } from '@fp-ts/core/Function' * * const result = pipe( * E.Do, * E.bind("a", () => E.left("e1")), * E.andThenBind("b", E.left("e2")) * ) * * assert.deepStrictEqual(result, E.left("e1")) * * @category do notation * @since 1.0.0 */ export const andThenBind: { ( name: Exclude, that: Either ): ( self: Either ) => Either ( self: Either, name: Exclude, that: Either ): Either } = semiProduct.andThenBind(SemiProduct)