// tracing: off

/**
 * adapted from https://github.com/gcanti/fp-ts
 */
import * as Tp from "../Collections/Immutable/Tuple"
import { isNone } from "../Option/core"
import * as St from "../Structural"

const _leftHash = /*#__PURE__*/ St.hashString("@effect-ts/system/Either/Left")

const _rightHash = /*#__PURE__*/ St.hashString("@effect-ts/system/Either/Right")
/**
 * Definitions
 */

export class Left {
  constructor(left) {
    this.left = left
    this._tag = "Left"
  }

  [St.equalsSym](that) {
    return that instanceof Left && St.equals(this.left, that.left)
  }

  get [St.hashSym]() {
    return St.combineHash(_leftHash, St.hash(this.left))
  }
}
export class Right {
  constructor(right) {
    this.right = right
    this._tag = "Right"
  }

  [St.equalsSym](that) {
    return that instanceof Right && St.equals(this.right, that.right)
  }

  get [St.hashSym]() {
    return St.combineHash(_rightHash, St.hash(this.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) {
  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) {
  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) {
  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) {
  return new Left(e)
}
/**
 * Widen left side `Either[E, A] => Either[E | E1, A]`
 */

export function widenE() {
  return (
    /**
     * @optimize identity
     */
    (self) => self
  )
}
/**
 * Widen right side `Either[E, A] => Either[E, A | A1]`
 */

export function widenA() {
  return (
    /**
     * @optimize identity
     */
    (self) => self
  )
}
/**
 * Alternatively construct that if self is left
 */

export function alt_(self, that) {
  return isLeft(self) ? that() : self
}
/**
 * Alternatively construct that if self is left
 *
 * @dataFirst alt_
 */

export function alt(that) {
  return (self) => alt_(self, that)
}
/**
 * Classic Applicative
 */

export function ap_(fab, fa) {
  return isLeft(fab) ? fab : isLeft(fa) ? fa : right(fab.right(fa.right))
}
/**
 * Classic Applicative
 *
 * @dataFirst ap_
 */

export function ap(fa) {
  return (fab) => ap_(fab, fa)
}
/**
 * Apply both and return both
 */

export function zip_(fa, fb) {
  return chain_(fa, (a) => map_(fb, (b) => Tp.tuple(a, b)))
}
/**
 * Apply both and return both
 *
 * @dataFirst zip_
 */

export function zip(fb) {
  return (fa) => zip_(fa, fb)
}
/**
 * Apply both and return first
 *
 * @dataFirst zipFirst_
 */

export function zipFirst(fb) {
  return (fa) => zipFirst_(fa, fb)
}
/**
 * Apply both and return first
 */

export function zipFirst_(fa, fb) {
  return ap_(
    map_(fa, (a) => () => a),
    fb
  )
}
/**
 * Apply both and return second
 *
 * @dataFirst zipSecond_
 */

export function zipSecond(fb) {
  return (fa) => zipSecond_(fa, fb)
}
/**
 * Apply both and return second
 */

export function zipSecond_(fa, fb) {
  return ap_(
    map_(fa, () => (b) => b),
    fb
  )
}
/**
 * Maps both left and right
 */

export function bimap_(fea, f, g) {
  return isLeft(fea) ? left(f(fea.left)) : right(g(fea.right))
}
/**
 * Maps both left and right
 *
 * @dataFirst bimap_
 */

export function bimap(f, g) {
  return (fa) => 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, f) {
  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.
 *
 * @dataFirst chain_
 */

export function chain(f) {
  return (ma) => chain_(ma, f)
}
/**
 * Like chain but ignores the constructed outout
 *
 * @dataFirst tap_
 */

export function tap(f) {
  return (ma) => chain_(ma, (a) => map_(f(a), () => a))
}
/**
 * Like chain but ignores the constructed outout
 */

export function tap_(ma, f) {
  return chain_(ma, (a) => map_(f(a), () => a))
}
/**
 * Self embed `Either[E, A]` into `Either[E, Either[E, A]]`
 */

export function duplicate(ma) {
  return extend_(ma, (x) => x)
}
/**
 * Returns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value.
 *
 * @dataFirst exists_
 */

export function exists(predicate) {
  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, predicate) {
  return isLeft(ma) ? false : predicate(ma.right)
}
/**
 * Apply `Either[E, A] => B` in case self is right returning `Either[E, B]`
 */

export function extend_(wa, f) {
  return isLeft(wa) ? wa : right(f(wa))
}
/**
 * Apply `Either[E, A] => B` in case self is right returning `Either[E, B]`
 *
 * @dataFirst extend_
 */

export function extend(f) {
  return (ma) => extend_(ma, f)
}
export function filterOrElse(predicate, onFalse) {
  return (ma) => chain_(ma, (a) => (predicate(a) ? right(a) : left(onFalse(a))))
}
export function filterOrElse_(ma, predicate, onFalse) {
  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) {
  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.
 *
 * @dataFirst fold_
 */

export function fold(onLeft, onRight) {
  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, onLeft, onRight) {
  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`
 *
 * @dataFirst fromNullable_
 */

export function fromNullable(e) {
  return (a) => (a == null ? left(e()) : right(a))
}
/**
 * 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, e) {
  return a == null ? left(e()) : right(a)
}
/**
 * Construct `Either[E, A]` from `Option[A]` constructing `E` with `onNone`
 *
 * @dataFirst fromOption_
 */

export function fromOption(onNone) {
  return (ma) => (isNone(ma) ? left(onNone()) : right(ma.value))
}
/**
 * Construct `Either[E, A]` from `Option[A]` constructing `E` with `onNone`
 */

export function fromOption_(ma, onNone) {
  return isNone(ma) ? left(onNone()) : right(ma.value)
}
export function fromPredicate(predicate, onFalse) {
  return (a) => (predicate(a) ? right(a) : left(onFalse(a)))
}
export function fromPredicate_(a, predicate, onFalse) {
  return predicate(a) ? right(a) : left(onFalse(a))
}
/**
 * Get `A` or in case self is left return `onLeft` result
 *
 * @dataFirst getOrElse_
 */

export function getOrElse(onLeft) {
  return (ma) => getOrElse_(ma, onLeft)
}
/**
 * Get `A` or in case self is left return `onLeft` result
 */

export function getOrElse_(self, onLeft) {
  return isLeft(self) ? onLeft(self.left) : self.right
}
/**
 * Returns `true` if the either is an instance of `Left`, `false` otherwise
 */

export function isLeft(ma) {
  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) {
  return ma._tag === "Right" ? true : false
}
/**
 * Use `A => B` to transform `Either[E, A]` to `Either[E, B]`
 */

export function map_(fa, f) {
  return isLeft(fa) ? fa : right(f(fa.right))
}
/**
 * Use `A => B` to transform `Either[E, A]` to `Either[E, B]`
 *
 * @dataFirst map_
 */

export function map(f) {
  return (fa) => map_(fa, f)
}
/**
 * Use `E => E1` to transform `Either[E, A]` to `Either[E1, A]`
 */

export function mapLeft_(fea, f) {
  return isLeft(fea) ? left(f(fea.left)) : fea
}
/**
 * Use `E => E1` to transform `Either[E, A]` to `Either[E1, A]`
 *
 * @dataFirst mapLeft_
 */

export function mapLeft(f) {
  return (fa) => mapLeft_(fa, f)
}
/**
 * Merges Left<E> | Right<B> into A | B
 */

export function merge(self) {
  return fold_(
    self,
    (x) => x,
    (x) => x
  )
}
/**
 * Alternatively run onLeft
 *
 * @dataFirst orElse_
 */

export function orElse(onLeft) {
  return (ma) => orElse_(ma, onLeft)
}
/**
 * Alternatively run onLeft
 */

export function orElse_(ma, onLeft) {
  return isLeft(ma) ? onLeft(ma.left) : ma
}
/**
 * Alternatively run onLeft returning
 *
 * @dataFirst orElseEither_
 */

export function orElseEither(onLeft) {
  return (ma) => orElseEither_(ma, onLeft)
}
/**
 * Alternatively run onLeft returning
 */

export function orElseEither_(ma, onLeft) {
  return orElse_(map_(ma, left), (e) => map_(onLeft(e), right))
}
/**
 * Converts a JavaScript Object Notation (JSON) string into an object.
 */

export function parseJSON_(s, onError) {
  return tryCatch(() => JSON.parse(s), onError)
}
/**
 * Converts a JavaScript Object Notation (JSON) string into an object.
 *
 * @dataFirst parseJSON_
 */

export function parseJSON(onError) {
  return (s) => tryCatch(() => JSON.parse(s), onError)
}
/**
 * Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
 */

export function stringifyJSON_(u, onError) {
  return tryCatch(() => JSON.stringify(u), onError)
}
/**
 * Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
 *
 * @dataFirst stringifyJSON_
 */

export function stringifyJSON(onError) {
  return (u) => tryCatch(() => JSON.stringify(u), onError)
}
/**
 * Inverts `Either[E, A]` into `Either[A, E]`
 */

export function swap(ma) {
  return isLeft(ma) ? right(ma.left) : left(ma.right)
}
/**
 * Default value for the `onError` argument of `tryCatch`
 */

export function toError(e) {
  return e instanceof Error ? e : new Error(String(e))
}
/**
 * Constructs a new `Either` from a function that might throw
 */

export function tryCatch(f, onError) {
  try {
    return right(f())
  } catch (e) {
    return left(onError(e))
  }
}
/**
 * Compact types Either<E, A> | Either<E2, B> = Either<E | E2, A | B>
 *
 * @optimize identity
 */

export function compact(_) {
  return _
}
/**
 * Reduce a value `b` through an `Either`
 */

export function reduce_(fa, b, f) {
  return isLeft(fa) ? b : f(b, fa.right)
}
/**
 * Reduce a value `b` through an `Either`
 *
 * @dataFirst reduce_
 */

export function reduce(b, f) {
  return (fa) => reduce_(fa, b, f)
}
/**
 * Reduce a value `b` through an `Either` in inverted order
 *
 * @dataFirst reduceRight_
 */

export function reduceRight(b, f) {
  return (fa) => reduceRight_(fa, b, f)
}
/**
 * Reduce a value `b` through an `Either` in inverted order
 */

export function reduceRight_(fa, b, f) {
  return isLeft(fa) ? b : f(fa.right, b)
}
//# sourceMappingURL=core.js.map
