// ets_tracing: off /** * adapted from https://github.com/gcanti/fp-ts */ import * as Tp from "../Collections/Immutable/Tuple/index.js" import type { Lazy, Predicate, Refinement } from "../Function/core.js" import type { Option } from "../Option/core.js" import { isNone } from "../Option/core.js" import * as St from "../Structural/index.js" const _leftHash = St.hashString("@effect-ts/system/Either/Left") const _rightHash = St.hashString("@effect-ts/system/Either/Right") /** * Definitions */ export class Left { readonly _tag = "Left" constructor(readonly left: E) {} [St.equalsSym](that: unknown): boolean { return that instanceof Left && St.equals(this.left, that.left) } get [St.hashSym](): number { return St.combineHash(_leftHash, St.hash(this.left)) } } export class Right { readonly _tag = "Right" constructor(readonly right: A) {} [St.equalsSym](that: unknown): boolean { return that instanceof Right && St.equals(this.right, that.right) } get [St.hashSym](): number { return St.combineHash(_rightHash, St.hash(this.right)) } } export type Either = Left | Right /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure */ export function right(a: A): Either { return new Right(a) } /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure */ export function rightW(a: A): Either { return new Right(a) } /** * Constructs a new `Either` holding a `Left` value. This usually represents a failure, due to the right-bias of this * structure */ export function left(e: E): Either { return new Left(e) } /** * Constructs a new `Either` holding a `Left` value. This usually represents a failure, due to the right-bias of this * structure */ export function leftW(e: E): Either { return new Left(e) } /** * Widen left side `Either[E, A] => Either[E | E1, A]` */ export function widenE() { return ( /** * @ets_optimize identity */ (self: Either): Either => self ) } /** * Widen right side `Either[E, A] => Either[E, A | A1]` */ export function widenA() { return ( /** * @ets_optimize identity */ (self: Either): Either => self ) } /** * Alternatively construct `that` if `self` is left */ export function alt_( self: Either, that: () => Either ): Either { return isLeft(self) ? that() : self } /** * Alternatively construct `that` if `self` is left * * @ets_data_first alt_ */ export function alt(that: () => Either) { return (self: Either): Either => alt_(self, that) } /** * Classic Applicative */ export function ap_( fab: Either B>, fa: Either ): Either { return isLeft(fab) ? fab : isLeft(fa) ? fa : right(fab.right(fa.right)) } /** * Classic Applicative * * @ets_data_first ap_ */ export function ap(fa: Either) { return (fab: Either B>): Either => ap_(fab, fa) } /** * Apply both and return both */ export function zip_( fa: Either, fb: Either ): Either> { return chain_(fa, (a) => map_(fb, (b) => Tp.tuple(a, b))) } /** * Apply both and return both * * @ets_data_first zip_ */ export function zip(fb: Either) { return (fa: Either): Either> => zip_(fa, fb) } /** * Apply both and return first * * @ets_data_first zipFirst_ */ export function zipFirst(fb: Either) { return (fa: Either): Either => zipFirst_(fa, fb) } /** * Apply both and return first */ export function zipFirst_( fa: Either, fb: Either ): Either { return ap_( map_(fa, (a) => () => a), fb ) } /** * Apply both and return second * * @ets_data_first zipSecond_ */ export function zipSecond(fb: Either) { return (fa: Either): Either => zipSecond_(fa, fb) } /** * Apply both and return second */ export function zipSecond_( fa: Either, fb: Either ): Either { return ap_( map_(fa, () => (b: B) => b), fb ) } /** * Maps both left and right */ export function bimap_( fea: Either, f: (e: E) => G, g: (a: A) => B ): Either { return isLeft(fea) ? left(f(fea.left)) : right(g(fea.right)) } /** * Maps both left and right * * @ets_data_first bimap_ */ export function bimap(f: (e: E) => G, g: (a: A) => B) { return (fa: Either): Either => bimap_(fa, f, g) } /** * Extends this computation with another computation that depends on the * result of this computation by running the first computation, using its * result to generate a second computation, and running that computation. */ export function chain_( fa: Either, f: (a: A) => Either ): Either { return isLeft(fa) ? fa : f(fa.right) } /** * Extends this computation with another computation that depends on the * result of this computation by running the first computation, using its * result to generate a second computation, and running that computation. * * @ets_data_first chain_ */ export function chain(f: (a: A) => Either) { return (ma: Either): Either => chain_(ma, f) } /** * Like chain but ignores the constructed outout * * @ets_data_first tap_ */ export function tap(f: (a: A) => Either) { return (ma: Either): Either => chain_(ma, (a) => map_(f(a), () => a)) } /** * Like chain but ignores the constructed outout */ export function tap_( ma: Either, f: (a: A) => Either ): Either { return chain_(ma, (a) => map_(f(a), () => a)) } /** * Self embed `Either[E, A]` into `Either[E, Either[E, A]]` */ export function duplicate(ma: Either): Either> { return extend_(ma, (x) => x) } /** * Returns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value. * * @ets_data_first exists_ */ export function exists(predicate: Predicate): (ma: Either) => boolean { return (ma) => (isLeft(ma) ? false : predicate(ma.right)) } /** * Returns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value. */ export function exists_(ma: Either, predicate: Predicate): boolean { return isLeft(ma) ? false : predicate(ma.right) } /** * Apply `Either[E, A] => B` in case `self` is `Right` returning `Either[E, B]` */ export function extend_( self: Either, f: (wa: Either) => B ): Either { return isLeft(self) ? self : right(f(self)) } /** * Apply `Either[E, A] => B` in case `self` is `Right` returning `Either[E, B]` * * @ets_data_first extend_ */ export function extend(f: (fa: Either) => B) { return (self: Either): Either => extend_(self, f) } /** * Apply predicate to `A` and construct `E` in case the predicate is `false` * * @ets_data_first filterOrElse_ */ export function filterOrElse( refinement: Refinement, onFalse: (a: A) => E ): (ma: Either) => Either export function filterOrElse( predicate: Predicate, onFalse: (a: A) => E ): (ma: Either) => Either export function filterOrElse(predicate: Predicate, onFalse: (a: A) => E) { return (ma: Either): Either => chain_(ma, (a) => (predicate(a) ? right(a) : left(onFalse(a)))) } /** * Apply predicate to `A` and construct `E` in case the predicate is `false` */ export function filterOrElse_( ma: Either, refinement: Refinement, onFalse: (a: A) => E ): Either export function filterOrElse_( ma: Either, predicate: Predicate, onFalse: (a: A) => E ): Either export function filterOrElse_( ma: Either, predicate: Predicate, onFalse: (a: A) => E ): Either { return chain_(ma, (a) => (predicate(a) ? right(a) : left(onFalse(a)))) } /** * Flatten nested `Either[E, Either[E1, A]]` into `Either[E | E1, A]` */ export function flatten(mma: Either>): Either { return chain_(mma, (x) => x) } /** * 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. * * @ets_data_first fold_ */ export function fold( onLeft: (e: E) => B, onRight: (a: A) => C ): (ma: Either) => B | C { return (ma) => (isLeft(ma) ? onLeft(ma.left) : onRight(ma.right)) } /** * 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. */ export function fold_( ma: Either, onLeft: (e: E) => B, onRight: (a: A) => C ): B | C { return isLeft(ma) ? onLeft(ma.left) : onRight(ma.right) } /** * Takes a 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` * * @ets_data_first fromNullable_ */ export function fromNullable(e: Lazy): (a: A) => Either> { return (a: A) => (a == null ? left(e()) : right(a as NonNullable)) } /** * Takes a 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` */ export function fromNullable_(a: A, e: Lazy): Either> { return a == null ? left(e()) : right(a as NonNullable) } /** * Construct `Either[E, A]` from `Option[A]` constructing `E` with `onNone` * * @ets_data_first fromOption_ */ export function fromOption(onNone: () => E) { return (ma: Option): Either => isNone(ma) ? left(onNone()) : right(ma.value) } /** * Construct `Either[E, A]` from `Option[A]` constructing `E` with `onNone` */ export function fromOption_(ma: Option, onNone: () => E): Either { return isNone(ma) ? left(onNone()) : right(ma.value) } /** * Construct `Either[E, A]` by applying a predicate to `A` and constructing * `E` if the predicate is false * * @ets_data_first fromPredicate_ */ export function fromPredicate( refinement: Refinement, onFalse: (a: A) => E ): (a: A) => Either export function fromPredicate( predicate: Predicate, onFalse: (a: A) => E ): (a: A) => Either export function fromPredicate(predicate: Predicate, onFalse: (a: A) => E) { return (a: A): Either => (predicate(a) ? right(a) : left(onFalse(a))) } /** * Construct `Either[E, A]` by applying a predicate to `A` and constructing * `E` if the predicate is false */ export function fromPredicate_( a: A, refinement: Refinement, onFalse: (a: A) => E ): Either export function fromPredicate_( a: A, predicate: Predicate, onFalse: (a: A) => E ): Either export function fromPredicate_( a: A, predicate: Predicate, onFalse: (a: A) => E ): Either { return predicate(a) ? right(a) : left(onFalse(a)) } /** * Get `A` or in case self is left return `onLeft` result * * @ets_data_first getOrElse_ */ export function getOrElse(onLeft: (e: E) => A): (self: Either) => A | B { return (ma) => getOrElse_(ma, onLeft) } /** * Get `A` or in case self is left return `onLeft` result */ export function getOrElse_(self: Either, onLeft: (e: E) => A): A | B { return isLeft(self) ? onLeft(self.left) : self.right } /** * Returns `true` if the either is an instance of `Left`, `false` otherwise */ export function isLeft(ma: Either): ma is Left { switch (ma._tag) { case "Left": return true case "Right": return false } } /** * Returns `true` if the either is an instance of `Right`, `false` otherwise */ export function isRight(ma: Either): ma is Right { return ma._tag === "Right" ? true : false } /** * Use `A => B` to transform `Either[E, A]` to `Either[E, B]` */ export function map_(fa: Either, f: (a: A) => B): Either { return isLeft(fa) ? fa : right(f(fa.right)) } /** * Use `A => B` to transform `Either[E, A]` to `Either[E, B]` * * @ets_data_first map_ */ export function map(f: (a: A) => B) { return (fa: Either): Either => map_(fa, f) } /** * Use `E => E1` to transform `Either[E, A]` to `Either[E1, A]` */ export function mapLeft_(fea: Either, f: (e: E) => G): Either { return isLeft(fea) ? left(f(fea.left)) : fea } /** * Use `E => E1` to transform `Either[E, A]` to `Either[E1, A]` * * @ets_data_first mapLeft_ */ export function mapLeft(f: (e: E) => G) { return (fa: Either): Either => mapLeft_(fa, f) } /** * Merges `Left | Right` into `A | B` */ export function merge(self: Either): E | A { return fold_( self, (x) => x, (x) => x ) } /** * Alternatively run onLeft * * @ets_data_first orElse_ */ export function orElse( onLeft: (e: E) => Either ): (ma: Either) => Either { return (ma) => orElse_(ma, onLeft) } /** * Alternatively run onLeft */ export function orElse_( ma: Either, onLeft: (e: E) => Either ): Either { return isLeft(ma) ? onLeft(ma.left) : ma } /** * Alternatively run onLeft returning * * @ets_data_first orElseEither_ */ export function orElseEither(onLeft: (e: E) => Either) { return (ma: Either) => orElseEither_(ma, onLeft) } /** * Alternatively run onLeft returning */ export function orElseEither_( ma: Either, onLeft: (e: E) => Either ): Either> { return orElse_(map_(ma, left), (e) => map_(onLeft(e), right)) } /** * Converts a JavaScript Object Notation (JSON) string into an object. */ export function parseJSON_( s: string, onError: (reason: unknown) => E ): Either { return tryCatch(() => JSON.parse(s), onError) } /** * Converts a JavaScript Object Notation (JSON) string into an object. * * @ets_data_first parseJSON_ */ export function parseJSON( onError: (reason: unknown) => E ): (s: string) => Either { return (s) => tryCatch(() => JSON.parse(s), onError) } /** * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. */ export function stringifyJSON_( u: unknown, onError: (reason: unknown) => E ): Either { return tryCatch(() => JSON.stringify(u), onError) } /** * Converts a JavaScript value to a JavaScript Object Notation (JSON) string. * * @ets_data_first stringifyJSON_ */ export function stringifyJSON( onError: (reason: unknown) => E ): (u: unknown) => Either { return (u) => tryCatch(() => JSON.stringify(u), onError) } /** * Inverts `Either[E, A]` into `Either[A, E]` */ export function swap(ma: Either): Either { return isLeft(ma) ? right(ma.left) : left(ma.right) } /** * Default value for the `onError` argument of `tryCatch` */ export function toError(e: unknown): Error { return e instanceof Error ? e : new Error(String(e)) } /** * Constructs a new `Either` from a function that might throw */ export function tryCatch(f: Lazy, onError: (e: unknown) => E): Either { try { return right(f()) } catch (e) { return left(onError(e)) } } /** * Compact types `Either | Either = Either` * * @ets_optimize identity */ export function compact>( _: E ): [E] extends [Either] ? Either : E { return _ as any } /** * Reduce a value `b` through an `Either` */ export function reduce_(fa: Either, b: B, f: (b: B, a: A) => B): B { return isLeft(fa) ? b : f(b, fa.right) } /** * Reduce a value `b` through an `Either` * * @ets_data_first reduce_ */ export function reduce(b: B, f: (b: B, a: A) => B): (fa: Either) => B { return (fa) => reduce_(fa, b, f) } /** * Reduce a value `b` through an `Either` in inverted order * * @ets_data_first reduceRight_ */ export function reduceRight(b: B, f: (a: A, b: B) => B) { return (fa: Either): B => reduceRight_(fa, b, f) } /** * Reduce a value `b` through an `Either` in inverted order */ export function reduceRight_(fa: Either, b: B, f: (a: A, b: B) => B): B { return isLeft(fa) ? b : f(fa.right, b) }