/** * @since 1.0.0 */ import type { Either, Left, Right } from "@fp-ts/core/Either" import * as E from "@fp-ts/core/Either" 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 { proto, structural } from "@fp-ts/core/internal/effect" import * as N from "@fp-ts/core/Number" import * as O from "@fp-ts/core/Option" import type { Option } from "@fp-ts/core/Option" import type { Predicate, Refinement } from "@fp-ts/core/Predicate" import type { NonEmptyReadonlyArray } from "@fp-ts/core/ReadonlyArray" import * as RA from "@fp-ts/core/ReadonlyArray" 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 type * 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 semiProduct from "@fp-ts/core/typeclass/SemiProduct" import * as traversable from "@fp-ts/core/typeclass/Traversable" /** * @category model * @since 1.0.0 */ export interface Both { readonly _tag: "Both" readonly left: E readonly right: A } /** * @category model * @since 1.0.0 */ export type These = Either | Both /** * @category model * @since 1.0.0 */ export type Validated = These, A> /** * @category type lambdas * @since 1.0.0 */ export interface TheseTypeLambda extends TypeLambda { readonly type: These } /** * @category type lambdas * @since 3.0.0 */ export interface ValidatedTypeLambda extends TypeLambda { readonly type: Validated } /** * @category constructors * @since 1.0.0 */ export const left: (left: E) => These = E.left /** * @category constructors * @since 1.0.0 */ export const right: (right: A) => These = E.right /** * Alias of {@link right}. * * @category constructors * @since 1.0.0 */ export const of: (right: A) => These = right /** * @category constructors * @since 1.0.0 */ export const both = (left: E, right: A): These => Object.setPrototypeOf({ _tag: "Both", left, right }, proto) /** * @category constructors * @since 1.0.0 */ export const leftOrBoth: { (onSome: LazyArg): (self: Option) => These (self: Option, onSome: LazyArg): These } = dual( 2, (self: Option, onSome: LazyArg): These => O.isNone(self) ? left(onSome()) : both(onSome(), self.value) ) /** * @category constructors * @since 1.0.0 */ export const rightOrBoth: { (onNone: LazyArg): (self: Option) => These (self: Option, onNone: LazyArg): These } = dual( 2, (self: Option, onNone: LazyArg): These => O.isNone(self) ? right(onNone()) : both(self.value, onNone()) ) /** * @category constructors * @since 1.0.0 */ export const fail = (e: E): Validated => left([e]) /** * @category constructors * @since 1.0.0 */ export const warn = (e: E, a: A): Validated => both([e], a) // ------------------------------------------------------------------------------------- // equivalence // ------------------------------------------------------------------------------------- /** * @category equivalence * @since 1.0.0 */ export const getEquivalence = ( EE: Equivalence, EA: Equivalence ): Equivalence> => equivalence.make((x, y) => isLeft(x) ? isLeft(y) && EE(x.left, y.left) : isRight(x) ? isRight(y) && EA(x.right, y.right) : isBoth(y) && EE(x.left, y.left) && EA(x.right, y.right) ) /** * @category pattern matching * @since 1.0.0 */ export const match: { ( onLeft: (e: E) => B, onRight: (a: A) => C, onBoth: (e: E, a: A) => D ): (self: These) => B | C | D ( self: These, onLeft: (e: E) => B, onRight: (a: A) => C, onBoth: (e: E, a: A) => D ): B | C | D } = dual(4, ( self: These, onLeft: (e: E) => B, onRight: (a: A) => C, onBoth: (e: E, a: A) => D ): B | C | D => { switch (self._tag) { case "Left": return onLeft(self.left) case "Right": return onRight(self.right) case "Both": return onBoth(self.left, self.right) } }) /** * @since 1.0.0 */ export const reverse: (self: These) => These = match( right, left, (e, a) => both(a, e) ) // ------------------------------------------------------------------------------------- // guards // ------------------------------------------------------------------------------------- /** * Tests if a value is a `These`. * * @param input - The value to check. * * @category guards * @since 1.0.0 */ export const isThese = (input: unknown): input is These => typeof input === "object" && input != null && structural in input && "_tag" in input && (input["_tag"] === "Left" || input["_tag"] === "Right" || input["_tag"] === "Both") /** * Determine if a `These` is a `Left`. * * @param self - The `These` to check. * * @example * import { isLeft, left, right, both } from '@fp-ts/core/These' * * assert.deepStrictEqual(isLeft(right(1)), false) * assert.deepStrictEqual(isLeft(left("error")), true) * assert.deepStrictEqual(isLeft(both("error", 1)), false) * * @category guards * @since 1.0.0 */ export const isLeft = (self: These): self is Left => self._tag === "Left" /** * Determine if a `These` is a `Left` or a `Both`. * * @param self - The `These` to check. * * @example * import { isLeftOrBoth, left, right, both } from '@fp-ts/core/These' * * assert.deepStrictEqual(isLeftOrBoth(right(1)), false) * assert.deepStrictEqual(isLeftOrBoth(left("error")), true) * assert.deepStrictEqual(isLeftOrBoth(both("error", 1)), true) * * @category guards * @since 1.0.0 */ export const isLeftOrBoth = (self: These): self is Left | Both => self._tag !== "Right" /** * Determine if a `These` is a `Right`. * * @param self - The `These` to check. * * @example * import { isRight, left, right, both } from '@fp-ts/core/These' * * assert.deepStrictEqual(isRight(right(1)), true) * assert.deepStrictEqual(isRight(left("error")), false) * assert.deepStrictEqual(isRight(both("error", 1)), false) * * @category guards * @since 1.0.0 */ export const isRight = (self: These): self is Right => self._tag === "Right" /** * Determine if a `These` is a `Right` or a `Both`. * * @param self - The `These` to check. * * @example * import { isRightOrBoth, left, right, both } from '@fp-ts/core/These' * * assert.deepStrictEqual(isRightOrBoth(right(1)), true) * assert.deepStrictEqual(isRightOrBoth(left("error")), false) * assert.deepStrictEqual(isRightOrBoth(both("error", 1)), true) * * @category guards * @since 1.0.0 */ export const isRightOrBoth = (self: These): self is Right | Both => self._tag !== "Left" /** * Determine if a `These` is a `Both`. * * @param self - The `These` to check. * * @example * import { isBoth, left, right, both } from '@fp-ts/core/These' * * assert.deepStrictEqual(isBoth(right(1)), false) * assert.deepStrictEqual(isBoth(left("error")), false) * assert.deepStrictEqual(isBoth(both("error", 1)), true) * * @category guards * @since 1.0.0 */ export const isBoth = (self: These): self is Both => self._tag === "Both" /** * Lifts a function that may throw to one returning a `These`. * * @category interop * @since 1.0.0 */ export const liftThrowable: , B, E>( f: (...a: A) => B, onThrow: (error: unknown) => E ) => ((...a: A) => These) = E.liftThrowable /** * Extracts the value of a `These` or throws if the `These` 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 `These` to extract the value from. * @param onLeft - A function that will be called if the `These` is `Left`. It returns the error to be thrown. * * @example * import * as E from "@fp-ts/core/These" * * assert.deepStrictEqual( * E.getOrThrowWith(E.right(1), () => new Error('Unexpected Left')), * 1 * ) * assert.deepStrictEqual( * E.getOrThrowWith(E.both("warning", 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: These) => A (self: These, onLeft: (e: E) => unknown): A } = dual(2, (self: These, onLeft: (e: E) => unknown): A => { if (isRightOrBoth(self)) { return self.right } throw onLeft(self.left) }) /** * Extracts the value of a `These` or throws if the `These` is `Left`. * * The thrown error is a default error. To configure the error thrown, see {@link getOrThrowWith}. * * @param self - The `These` to extract the value from. * @throws `Error("getOrThrow called on a Left")` * * @example * import * as T from "@fp-ts/core/These" * * assert.deepStrictEqual(T.getOrThrow(T.right(1)), 1) * assert.deepStrictEqual(T.getOrThrow(T.both("warning", 1)), 1) * assert.throws(() => T.getOrThrow(T.left("error"))) * * @category interop * @since 1.0.0 */ export const getOrThrow: (self: These) => A = getOrThrowWith(() => new Error("getOrThrow called on a Left") ) /** * Extracts the value of a `These` or throws if the `These` 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 `These` to extract the value from. * @param onLeft - A function that will be called if the `These` is `Left`. It returns the error to be thrown. * * @example * import * as E from "@fp-ts/core/These" * * assert.deepStrictEqual( * E.getRightOnlyOrThrowWith( * E.right(1), * () => new Error("Unexpected Left or Both") * ), * 1 * ) * assert.throws(() => E.getRightOnlyOrThrowWith(E.both("warning", 1), () => new Error("Unexpected Left or Both"))) * assert.throws(() => E.getRightOnlyOrThrowWith(E.left("error"), () => new Error("Unexpected Left or Both"))) * * @category interop * @since 1.0.0 */ export const getRightOnlyOrThrowWith: { (onLeftOrBoth: (e: E) => unknown): (self: These) => A (self: These, onLeftOrBoth: (e: E) => unknown): A } = dual(2, (self: These, onLeftOrBoth: (e: E) => unknown): A => { if (isRight(self)) { return self.right } throw onLeftOrBoth(self.left) }) /** * Extracts the value of a `These` or throws if the `These` is not a `Right`. * * The thrown error is a default error. To configure the error thrown, see {@link getRightOnlyOrThrowWith}. * * @param self - The `These` to extract the value from. * @throws `Error("getOrThrow called on a Left")` * * @example * import * as T from "@fp-ts/core/These" * * assert.deepStrictEqual(T.getRightOnlyOrThrow(T.right(1)), 1) * assert.throws(() => T.getRightOnlyOrThrow(T.both("error", 1))) * assert.throws(() => T.getRightOnlyOrThrow(T.left("error"))) * * @category interop * @since 1.0.0 */ export const getRightOnlyOrThrow: (self: These) => A = getRightOnlyOrThrowWith(() => new Error("getRightOnlyOrThrow called on a Left or a Both") ) /** * @category conversions * @since 1.0.0 */ export const fromNullable: { (onNullable: LazyArg): (a: A) => These> (a: A, onNullable: LazyArg): These> } = dual( 2, (a: A, onNullable: LazyArg): These> => a == null ? left(onNullable()) : right(a as NonNullable) ) /** * @category conversions * @since 1.0.0 */ export const fromEither = (self: Either): Validated => E.isLeft(self) ? left([self.left]) : self /** * @category conversions * @since 1.0.0 */ export const toEither: { (onBoth: (e: E, a: A) => Either): (self: These) => Either (self: These, onBoth: (e: E, a: A) => Either): Either } = dual( 2, (self: These, onBoth: (e: E, a: A) => Either): Either => isBoth(self) ? onBoth(self.left, self.right) : self ) /** * @category conversions * @since 1.0.0 */ export const absolve: (self: These) => Either = toEither(( _, a ) => E.right(a)) /** * @category conversions * @since 1.0.0 */ export const condemn: (self: These) => Either = toEither(( e, _ ) => E.left(e)) /** * @category lifting * @since 1.0.0 */ export const liftNullable = , B, E>( f: (...a: A) => B | null | undefined, onNullable: (...a: A) => E ) => (...a: A): These> => fromNullable(() => onNullable(...a))(f(...a)) /** * @category sequencing * @since 1.0.0 */ export const flatMapNullable: { ( f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): (self: Validated) => Validated> ( self: Validated, f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): Validated> } = dual(3, ( self: Validated, f: (a: A) => B | null | undefined, onNullable: (a: A) => E2 ): Validated> => flatMap(self, liftNullable(f, (a) => [onNullable(a)]))) /** * @category lifting * @since 1.0.0 */ export const liftPredicate: { ( refinement: Refinement, onFalse: (c: C) => E ): (c: C) => These (predicate: Predicate, onFalse: (b: B) => E): (b: B) => These } = (predicate: Predicate, onFalse: (b: B) => E) => (b: B) => predicate(b) ? right(b) : left(onFalse(b)) /** * @category conversions * @since 1.0.0 */ export const fromIterable: { (onEmpty: LazyArg): (collection: Iterable) => These (collection: Iterable, onEmpty: LazyArg): These } = dual(2, (collection: Iterable, onEmpty: LazyArg): These => { for (const a of collection) { return right(a) } return left(onEmpty()) }) /** * @category conversions * @since 1.0.0 */ export const fromOption: { (onNone: LazyArg): (self: Option) => These (self: Option, onNone: LazyArg): These } = dual( 2, (self: Option, onNone: LazyArg): These => O.isNone(self) ? left(onNone()) : right(self.value) ) /** * @category conversions * @since 1.0.0 */ export const fromTuple = (self: readonly [E, A]): These => both(self[0], self[1]) /** * @category lifting * @since 1.0.0 */ export const liftOption = , B, E>( f: (...a: A) => Option, onNone: (...a: A) => E ) => (...a: A): These => fromOption(() => onNone(...a))(f(...a)) /** * @category lifting * @since 1.0.0 */ export const liftEither = , E, B>( f: (...a: A) => Either ) => (...a: A): Validated => fromEither(f(...a)) /** * @category lifting * @since 1.0.0 */ export const liftThese = , E, B>( f: (...a: A) => These ) => (...a: A): Validated => toValidated(f(...a)) /** * @category sequencing * @since 1.0.0 */ export const flatMapOption: { ( f: (a: A) => Option, onNone: (a: A) => E2 ): (self: Validated) => Validated ( self: Validated, f: (a: A) => Option, onNone: (a: A) => E2 ): Validated } = dual(3, ( self: Validated, f: (a: A) => Option, onNone: (a: A) => E2 ): Validated => flatMap(self, liftOption(f, (a) => [onNone(a)]))) /** * @category sequencing * @since 1.0.0 */ export const flatMapEither: { (f: (a: A) => Either): (self: Validated) => Validated (self: Validated, f: (a: A) => Either): Validated } = dual( 2, (self: Validated, f: (a: A) => Either): Validated => flatMap(self, liftEither(f)) ) /** * @category sequencing * @since 1.0.0 */ export const flatMapThese: { (f: (a: A) => These): (self: Validated) => Validated (self: Validated, f: (a: A) => These): Validated } = dual( 2, (self: Validated, f: (a: A) => These): Validated => flatMap(self, liftThese(f)) ) /** * Converts a `These` to an `Option` discarding the error (`Both` included). * * @category getters * @since 1.0.0 */ export const getRight = ( self: These ): Option => isLeft(self) ? O.none() : O.some(self.right) /** * Returns the value if and only if the value is a `Right` (i.e. `Both` is excluded). * * @category getters * @since 1.0.0 */ export const getRightOnly = ( self: These ): Option => isRight(self) ? O.some(self.right) : O.none() /** * Converts a `These` to an `Option` discarding the value (`Both` included). * * @category getters * @since 1.0.0 */ export const getLeft = ( self: These ): Option => isRight(self) ? O.none() : O.some(self.left) /** * Returns the error if and only if the value is a `Left` (i.e. `Both` is excluded). * * @category getters * @since 1.0.0 */ export const getLeftOnly = ( self: These ): Option => isLeft(self) ? O.some(self.left) : O.none() /** * @category getters * @since 1.0.0 */ export const getBoth = ( self: These ): Option => isBoth(self) ? O.some([self.left, self.right]) : O.none() /** * @category getters * @since 1.0.0 */ export const getBothOrElse: { (e: LazyArg, a: LazyArg): (self: These) => [E, A] (self: These, e: LazyArg, a: LazyArg): [E, A] } = dual(3, (self: These, e: LazyArg, a: LazyArg): [E, A] => isLeft(self) ? [self.left, a()] : isRight(self) ? [e(), self.right] : [self.left, self.right]) /** * @category getters * @since 1.0.0 */ export const getOrElse: { (onLeft: LazyArg): (self: These) => A | B (self: These, onLeft: LazyArg): A | B } = dual( 2, (self: These, onLeft: LazyArg): A | B => isLeft(self) ? onLeft() : self.right ) /** * @category getters * @since 1.0.0 */ export const getOrNull: (self: These) => A | null = getOrElse(constNull) /** * @category getters * @since 1.0.0 */ export const getOrUndefined: (self: These) => A | undefined = getOrElse(constUndefined) /** * @category debugging * @since 1.0.0 */ export const inspectRight: { (onRight: (a: A) => void): (self: These) => These (self: These, onRight: (a: A) => void): These } = dual(2, (self: These, onRight: (a: A) => void): These => { if (isRight(self)) { onRight(self.right) } return self }) /** * @category debugging * @since 1.0.0 */ export const inspectRightOrBoth: { (onRightOrBoth: (a: A) => void): (self: These) => These (self: These, onRightOrBoth: (a: A) => void): These } = dual(2, (self: These, onRightOrBoth: (a: A) => void): These => { if (isRightOrBoth(self)) { onRightOrBoth(self.right) } return self }) /** * @category debugging * @since 1.0.0 */ export const inspectLeft: { (onLeft: (e: E) => void): (self: These) => These (self: These, onLeft: (e: E) => void): These } = dual(2, (self: These, onLeft: (e: E) => void): These => { if (isLeft(self)) { onLeft(self.left) } return self }) /** * @category debugging * @since 1.0.0 */ export const inspectBoth: { (onBoth: (e: E, a: A) => void): (self: These) => These (self: These, onBoth: (e: E, a: A) => void): These } = dual(2, (self: These, onBoth: (e: E, a: A) => void): These => { if (isBoth(self)) { onBoth(self.left, self.right) } return self }) /** * @category mapping * @since 1.0.0 */ export const bimap: { (f: (e: E1) => E2, g: (a: A) => B): (self: These) => These (self: These, f: (e: E1) => E2, g: (a: A) => B): These } = dual( 3, (self: These, f: (e: E1) => E2, g: (a: A) => B): These => isLeft(self) ? left(f(self.left)) : isRight(self) ? right(g(self.right)) : both(f(self.left), g(self.right)) ) /** * @category instances * @since 1.0.0 */ export const Bicovariant: bicovariant.Bicovariant = { bimap } /** * Maps the `Left` side of an `These` value to a new `These` value. * * @param self - The input `These` value to map. * @param f - A transformation function to apply to the `Left` value of the input `These`. * * @category error handling * @since 1.0.0 */ export const mapLeft: { (f: (e: E) => G): (self: These) => These (self: These, f: (e: E) => G): These } = bicovariant.mapLeft(Bicovariant) /** * @category conversions * @since 1.0.0 */ export const toValidated: (self: These) => Validated = mapLeft((e) => [e]) /** * Maps the `Right` side of an `These` value to a new `These` value. * * @param self - An `These` to map * @param f - The function to map over the value of the `These` * * @category mapping * @since 1.0.0 */ export const map: { (self: These, f: (a: A) => B): These (f: (a: A) => B): (self: These) => These } = bicovariant.map(Bicovariant) 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: These) => These = invariant.tupled( Invariant ) /** * @category mapping * @since 1.0.0 */ export const flap: { (a: A, self: These B>): These (self: These B>): (a: A) => These } = 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: These, b: B): These (b: B): (self: These) => These } = covariant.as(Covariant) /** * Returns the effect resulting from mapping the right of this effect to unit. * * @category mapping * @since 1.0.0 */ export const asUnit: (self: These) => These = covariant.asUnit(Covariant) /** * @category instances * @since 1.0.0 */ export const Of: of_.Of = { of } /** * @since 1.0.0 */ export const unit: These = of_.unit(Of) /** * @category instances * @since 1.0.0 */ export const Pointed: pointed.Pointed = { of, imap, map } /** * @category traversing * @since 1.0.0 */ export const traverse = ( F: applicative.Applicative ): { ( f: (a: A) => Kind ): (self: These) => Kind> ( self: These, f: (a: A) => Kind ): Kind> } => dual(2, ( self: These, f: (a: A) => Kind ): Kind> => isLeft(self) ? F.of>(self) : isRight(self) ? F.map>(f(self.right), right) : F.map(f(self.right), (b) => both(self.left, b))) /** * @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: These> ) => Kind> = traversable.sequence(Traversable) /** * @category traversing * @since 1.0.0 */ export const traverseTap: ( F: applicative.Applicative ) => { ( self: These, f: (a: A) => Kind ): Kind> ( f: (a: A) => Kind ): (self: These) => Kind> } = traversable.traverseTap(Traversable) /** * Returns a function that checks if a `These` 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: These) => boolean (self: These, a: A): boolean } => dual( 2, (self: These, a: A): boolean => isLeft(self) ? false : isEquivalent(self.right, a) ) /** * @category predicates * @since 1.0.0 */ export const exists: { (predicate: Predicate): (self: These) => boolean (self: These, predicate: Predicate): boolean } = dual( 2, (self: These, predicate: Predicate): boolean => isLeft(self) ? false : predicate(self.right) ) /** * @category instances * @since 1.0.0 */ export const Foldable: foldable.Foldable = { reduce: dual( 3, (self: These, b: B, f: (b: B, a: A) => B): B => isLeft(self) ? b : f(b, 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) => These): (self: These) => These (self: These, that: (e1: E1) => These): These } = dual( 2, (self: These, that: (e1: E1) => These): These => 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) => These ): (self: These) => These> ( self: These, that: (e1: E1) => These ): These> } = dual(2, ( self: These, that: (e1: E1) => These ): These> => isLeft(self) ? map(that(self.left), E.right) : map(self, E.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: These) => These (self: These, onLeft: LazyArg): These } = dual( 2, (self: These, onLeft: LazyArg): These => orElse(self, () => left(onLeft())) ) const coproduct = (self: These, that: These): These => isRightOrBoth(self) ? self : that const coproductMany = (self: These, collection: Iterable>): These => { let out = self if (isRightOrBoth(out)) { return out } for (out of collection) { if (isRightOrBoth(out)) { return out } } return out } /** * @category error handling * @since 1.0.0 */ export const firstRightOrBothOf: { (collection: Iterable>): (self: These) => These (self: These, collection: Iterable>): These } = dual(2, coproductMany) /** * @category instances * @since 1.0.0 */ export const SemiCoproduct: semiCoproduct.SemiCoproduct = { imap, coproduct, coproductMany } /** * @category combining * @since 1.0.0 */ export const getFirstRightOrBothSemigroup: () => Semigroup> = semiCoproduct .getSemigroup(SemiCoproduct) /** * @category instances * @since 1.0.0 */ export const SemiAlternative: semiAlternative.SemiAlternative = { map, imap, coproduct, coproductMany: firstRightOrBothOf } /** * @category filtering * @since 1.0.0 */ export const filter: { (refinement: Refinement, onFalse: LazyArg): ( self: These ) => These ( predicate: Predicate, onFalse: LazyArg ): (self: These) => These ( self: These, refinement: Refinement, onFalse: LazyArg ): These ( self: These, predicate: Predicate, onFalse: LazyArg ): These } = dual(3, ( self: These, predicate: Predicate, onFalse: LazyArg ): These => 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: These) => These ( self: These, f: (a: A) => Option, onNone: LazyArg ): These } = dual(3, ( self: These, f: (a: A) => Option, onNone: LazyArg ): These => { if (isLeft(self)) { return self } if (isRight(self)) { const ob = f(self.right) return O.isNone(ob) ? left(onNone()) : right(ob.value) } const ob = f(self.right) return O.isNone(ob) ? left(onNone()) : both(self.left, ob.value) }) /** * @category filtering * @since 1.0.0 */ export const compact: { (onNone: LazyArg): (self: These>) => These (self: These>, onNone: LazyArg): These } = dual( 2, (self: These>, onNone: LazyArg): These => filterMap(self, identity, onNone) ) const product = ( self: Validated, that: Validated ): Validated => { if (isLeft(self)) { return self } if (isRight(self)) { if (isLeft(that)) { return that } if (isRight(that)) { return right([self.right, that.right]) } return both(that.left, [self.right, that.right]) } if (isLeft(that)) { return left(RA.appendAllNonEmpty(self.left, that.left)) } if (isRight(that)) { return both(self.left, [self.right, that.right]) } return both(RA.appendAllNonEmpty(self.left, that.left), [self.right, that.right]) } const productAll = ( collection: Iterable> ): Validated> => { const rights: Array = [] const lefts: Array = [] let isFatal = false for (const t of collection) { if (isLeft(t)) { lefts.push(...t.left) isFatal = true break } else if (isRight(t)) { rights.push(t.right) } else { lefts.push(...t.left) rights.push(t.right) } } if (RA.isNonEmpty(lefts)) { return isFatal ? left(lefts) : both(lefts, rights) } return right(rights) } const productMany = ( self: Validated, collection: Iterable> ): Validated]> => map(product(self, productAll(collection)), ([a, as]) => [a, ...as]) /** * @category instances * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { imap, product, productMany } /** * @category instances * @since 1.0.0 */ export const SemiApplicative: semiApplicative.SemiApplicative = { imap, map, product, productMany } /** * Lifts a binary function into `These`. * * @param f - The function to lift. * * @category lifting * @since 1.0.0 */ export const lift2: (f: (a: A, b: B) => C) => { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = semiApplicative.lift2(SemiApplicative) /** * @category combining * @since 1.0.0 */ export const zipWith: { ( self: Validated, that: Validated, f: (a: A, b: B) => C ): Validated ( that: Validated, f: (a: A, b: B) => C ): (self: Validated) => Validated } = semiApplicative.zipWith(SemiApplicative) /** * @since 1.0.0 */ export const ap: { (self: Validated B>, that: Validated): Validated ( that: Validated ): (self: Validated B>) => Validated } = semiApplicative.ap(SemiApplicative) /** * @category combining * @since 1.0.0 */ export const getFirstLeftSemigroup: (S: Semigroup) => Semigroup> = semiApplicative.getSemigroup(SemiApplicative) /** * Appends an element to the end of a tuple. * * @since 1.0.0 */ export const appendElement: { , E2, B>( self: Validated, that: Validated ): Validated ( that: Validated ): >(self: Validated) => Validated } = semiProduct.appendElement(SemiProduct) /** * @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) => Validated< [T[number]] extends [Validated] ? E : never, { [I in keyof T]: [T[I]] extends [Validated] ? A : never } > = product_.tuple(Product) /** * @since 1.0.0 */ export const struct: >>( r: R ) => Validated< [R[keyof R]] extends [Validated] ? E : never, { [K in keyof R]: [R[K]] extends [Validated] ? A : never } > = product_ .struct(Product) /** * @category sequencing * @since 1.0.0 */ export const flatMap: { (f: (a: A) => Validated): (self: Validated) => Validated (self: Validated, f: (a: A) => Validated): Validated } = dual( 2, (self: Validated, f: (a: A) => Validated): Validated => { if (isLeft(self)) { return self } if (isRight(self)) { return f(self.right) } const that = f(self.right) if (isLeft(that)) { return left(RA.appendAllNonEmpty(that.left)(self.left)) } if (isRight(that)) { return both(self.left, that.right) } return both(RA.appendAllNonEmpty(that.left)(self.left), that.right) } ) /** * @category instances * @since 1.0.0 */ export const Applicative: applicative.Applicative = { imap, of, map, product, productMany, productAll } /** * @category combining * @since 1.0.0 */ export const getFirstLeftMonoid: (M: Monoid) => Monoid> = applicative .getMonoid( Applicative ) /** * @category instances * @since 1.0.0 */ export const FlatMap: flatMap_.FlatMap = { flatMap } /** * @since 1.0.0 */ export const flatten: ( self: Validated> ) => Validated = flatMap_.flatten(FlatMap) /** * @since 1.0.0 */ export const andThen: { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = flatMap_.andThen(FlatMap) /** * @since 1.0.0 */ export const composeKleisliArrow: { ( afb: (a: A) => Validated, bfc: (b: B) => Validated ): (a: A) => Validated ( bfc: (b: B) => Validated ): (afb: (a: A) => Validated) => (a: A) => Validated } = 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: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = chainable.andThenDiscard(Chainable) /** * Returns an effect that effectfully "peeks" at the success of this effect. * * @category combinators * @since 1.0.0 */ export const tap: { (self: Validated, f: (a: A) => Validated): Validated (f: (a: A) => Validated): (self: Validated) => Validated } = chainable.tap(Chainable) /** * @category instances * @since 1.0.0 */ export const Monad: monad.Monad = { imap, of, map, flatMap } // ------------------------------------------------------------------------------------- // algebraic operations // ------------------------------------------------------------------------------------- /** * @category algebraic operations * @since 1.0.0 */ export const sum: { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = lift2(N.sum) /** * @category algebraic operations * @since 1.0.0 */ export const multiply: { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = lift2(N.multiply) /** * @category algebraic operations * @since 1.0.0 */ export const subtract: { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = lift2(N.subtract) /** * @category algebraic operations * @since 1.0.0 */ export const divide: { (self: Validated, that: Validated): Validated (that: Validated): (self: Validated) => Validated } = lift2(N.divide) // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- /** * @category do notation * @since 1.0.0 */ export const bindTo: { (name: N): (self: These) => These (self: These, name: N): These } = invariant.bindTo(Invariant) const let_: { ( name: Exclude, f: (a: A) => B ): (self: These) => These ( self: These, name: Exclude, f: (a: A) => B ): These } = covariant.let(Covariant) export { /** * @category do notation * @since 1.0.0 */ let_ as let } /** * @category do notation * @since 1.0.0 */ export const Do: These = of_.Do(Of) /** * @category do notation * @since 1.0.0 */ export const bind: { ( name: Exclude, f: (a: A) => Validated ): ( self: Validated ) => Validated ( self: Validated, name: Exclude, f: (a: A) => Validated ): Validated } = chainable.bind(Chainable) /** * @category do notation * @since 1.0.0 */ export const bindEither: { ( name: Exclude, f: (a: A) => Either ): ( self: Validated ) => Validated ( self: Validated, name: Exclude, f: (a: A) => Either ): Validated } = dual(3, ( self: Validated, name: Exclude, f: (a: A) => Either ): Validated => bind(self, name, (a) => fromEither(f(a)))) /** * @category do notation * @since 1.0.0 */ export const bindThese: { ( name: Exclude, f: (a: A) => These ): ( self: Validated ) => Validated ( self: Validated, name: Exclude, f: (a: A) => These ): Validated } = dual(3, ( self: Validated, name: Exclude, f: (a: A) => These ): Validated => bind(self, name, (a) => toValidated(f(a)))) /** * @category do notation * @since 1.0.0 */ export const andThenBind: { ( name: Exclude, that: Validated ): ( self: Validated ) => Validated ( self: Validated, name: Exclude, that: Validated ): Validated } = semiProduct.andThenBind(SemiProduct)