"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.partial =
  exports.PartialException =
  exports.PartialExceptionTypeId =
  exports.tryCatch =
  exports.toUndefined =
  exports.toNullable =
  exports.mapNullable =
  exports.map =
  exports.map_ =
  exports.isSome =
  exports.isNone =
  exports.getRight =
  exports.getRefinement =
  exports.getOrElseInv_ =
  exports.getOrElse_ =
  exports.getOrElseInv =
  exports.getOrElse =
  exports.getLeft =
  exports.fromPredicate =
  exports.fromNullable =
  exports.fromEither =
  exports.fold_ =
  exports.fold =
  exports.extend_ =
  exports.extend =
  exports.exists =
  exports.duplicate =
  exports.flatten =
  exports.tap_ =
  exports.tap =
  exports.chain =
  exports.chain_ =
  exports.zipSecond =
  exports.zipFirst =
  exports.zip =
  exports.zip_ =
  exports.ap =
  exports.ap_ =
  exports.some =
  exports.emptyOf =
  exports.none =
  exports.Some =
  exports.None =
    void 0
// tracing: off
/* adapted from https://github.com/gcanti/fp-ts */
const Tp = __importStar(require("../Collections/Immutable/Tuple"))
const core_1 = require("../Function/core")
const St = __importStar(require("../Structural"))
const _noneHash = St.hashString("@effect-ts/system/Option/None")
const _someHash = St.hashString("@effect-ts/system/Option/Some")
/**
 * Definitions
 */
class None {
  constructor() {
    this._tag = "None"
  }
  [St.equalsSym](that) {
    return that instanceof None
  }
  get [St.hashSym]() {
    return _noneHash
  }
}
exports.None = None
class Some {
  constructor(value) {
    this.value = value
    this._tag = "Some"
  }
  [St.equalsSym](that) {
    return that instanceof Some && St.equals(this.value, that.value)
  }
  get [St.hashSym]() {
    return St.combineHash(_someHash, St.hash(this.value))
  }
}
exports.Some = Some
/**
 * Constructs none
 */
exports.none = new None()
/**
 * Constructs none
 */
const emptyOf = () => exports.none
exports.emptyOf = emptyOf
/**
 * Constructs Some(A)
 */
function some(a) {
  return new Some(a)
}
exports.some = some
/**
 * Classic applicative
 */
const ap_ = (mab, ma) =>
  isNone(mab) ? exports.none : isNone(ma) ? exports.none : some(mab.value(ma.value))
exports.ap_ = ap_
/**
 * Classic applicative
 */
const ap = (fa) => (fab) => (0, exports.ap_)(fab, fa)
exports.ap = ap
/**
 * Zips `Option[A]` and `Option[B]` into `Option[(A, B)]
 */
const zip_ = (fa, fb) =>
  (0, exports.chain_)(fa, (a) => (0, exports.map_)(fb, (b) => Tp.tuple(a, b)))
exports.zip_ = zip_
/**
 * Zips `Option[A]` and `Option[B]` into `Option[(A, B)]
 */
const zip = (fb) => (fa) => (0, exports.zip_)(fa, fb)
exports.zip = zip
/**
 * Apply both and return first
 */
const zipFirst = (fb) => (fa) =>
  (0, exports.ap_)(
    (0, exports.map_)(fa, (a) => () => a),
    fb
  )
exports.zipFirst = zipFirst
/**
 * Apply both and return second
 */
const zipSecond = (fb) => (fa) =>
  (0, exports.ap_)(
    (0, exports.map_)(fa, () => (b) => b),
    fb
  )
exports.zipSecond = zipSecond
/**
 * Builds a new option constructed using the value of self
 */
const chain_ = (ma, f) => (isNone(ma) ? exports.none : f(ma.value))
exports.chain_ = chain_
/**
 * Builds a new option constructed using the value of self
 */
const chain = (f) => (ma) => (0, exports.chain_)(ma, f)
exports.chain = chain
/**
 * Like chain but ignores the constructed outout
 */
const tap = (f) => (ma) =>
  (0, exports.chain_)(ma, (a) => (0, exports.map_)(f(a), () => a))
exports.tap = tap
/**
 * Like chain but ignores the constructed outout
 */
const tap_ = (ma, f) => (0, exports.chain_)(ma, (a) => (0, exports.map_)(f(a), () => a))
exports.tap_ = tap_
/**
 * Flattens nested options
 */
const flatten = (fa) => (0, exports.chain_)(fa, core_1.identity)
exports.flatten = flatten
/**
 * Wraps this option into a second one
 */
const duplicate = (ma) => (isNone(ma) ? exports.none : some(ma))
exports.duplicate = duplicate
/**
 * Returns `true` if the predicate is satisfied by the wrapped value
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     exists(n => n > 0)
 *   ),
 *   true
 * )
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     exists(n => n > 1)
 *   ),
 *   false
 * )
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     exists(n => n > 0)
 *   ),
 *   false
 * )
 */
function exists(predicate) {
  return (ma) => (isNone(ma) ? false : predicate(ma.value))
}
exports.exists = exists
/**
 * Apply `Option[A] => B` in case self is some returning `Option[B]`
 */
const extend = (f) => (self) => (0, exports.extend_)(self, f)
exports.extend = extend
/**
 * Apply `Option[A] => B` in case self is some returning `Option[B]`
 */
const extend_ = (self, f) => (isNone(self) ? exports.none : some(f(self)))
exports.extend_ = extend_
/**
 * Takes a default value, a function, and an `Option` value, if the `Option` value is `None` the default value is
 * returned, otherwise the function is applied to the value inside the `Some` and the result is returned.
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     fold(() => 'a none', a => `a some containing ${a}`)
 *   ),
 *   'a some containing 1'
 * )
 *
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     fold(() => 'a none', a => `a some containing ${a}`)
 *   ),
 *   'a none'
 * )
 */
function fold(onNone, onSome) {
  return (ma) => fold_(ma, onNone, onSome)
}
exports.fold = fold
/**
 * Takes a default value, a function, and an `Option` value, if the `Option` value is `None` the default value is
 * returned, otherwise the function is applied to the value inside the `Some` and the result is returned.
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     fold(() => 'a none', a => `a some containing ${a}`)
 *   ),
 *   'a some containing 1'
 * )
 *
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     fold(() => 'a none', a => `a some containing ${a}`)
 *   ),
 *   'a none'
 * )
 */
function fold_(ma, onNone, onSome) {
  return isNone(ma) ? onNone() : onSome(ma.value)
}
exports.fold_ = fold_
/**
 * Constructs `Option[A]` from `Either[E, A]` discarding `E`
 */
const fromEither = (ma) => (ma._tag === "Left" ? exports.none : some(ma.right))
exports.fromEither = fromEither
/**
 * Constructs a new `Option` from a nullable type. If the value is `null` or `undefined`, returns `None`, otherwise
 * returns the value wrapped in a `Some`
 *
 * @example
 * assert.deepStrictEqual(fromNullable(undefined), none)
 * assert.deepStrictEqual(fromNullable(null), none)
 * assert.deepStrictEqual(fromNullable(1), some(1))
 */
function fromNullable(a) {
  return a == null ? exports.none : some(a)
}
exports.fromNullable = fromNullable
function fromPredicate(predicate) {
  return (a) => (predicate(a) ? some(a) : exports.none)
}
exports.fromPredicate = fromPredicate
/**
 * Returns an `E` value if possible
 */
function getLeft(ma) {
  return ma._tag === "Right" ? exports.none : some(ma.left)
}
exports.getLeft = getLeft
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns the given default value
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     getOrElse(() => 0)
 *   ),
 *   1
 * )
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     getOrElse(() => 0)
 *   ),
 *   0
 * )
 */
function getOrElse(onNone) {
  return (o) => getOrElse_(o, onNone)
}
exports.getOrElse = getOrElse
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns the given default value
 */
function getOrElseInv(onNone) {
  return getOrElse(onNone)
}
exports.getOrElseInv = getOrElseInv
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns the given default value
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     getOrElse(() => 0)
 *   ),
 *   1
 * )
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     getOrElse(() => 0)
 *   ),
 *   0
 * )
 */
function getOrElse_(ma, onNone) {
  return ma._tag === "None" ? onNone() : ma.value
}
exports.getOrElse_ = getOrElse_
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns the given default value
 */
function getOrElseInv_(ma, onNone) {
  return getOrElse_(ma, onNone)
}
exports.getOrElseInv_ = getOrElseInv_
/**
 * Returns a `Refinement` (i.e. a custom type guard) from a `Option` returning function.
 * This function ensures that a custom type guard definition is type-safe.
 *
 * ```ts
 * type A = { type: 'A' }
 * type B = { type: 'B' }
 * type C = A | B
 *
 * const isA = (c: C): c is A => c.type === 'B' // <= typo but typescript doesn't complain
 * const isA = getRefinement<C, A>(c => (c.type === 'B' ? some(c) : none)) // static error: Type '"B"' is not assignable to type '"A"'
 * ```
 */
function getRefinement(getOption) {
  return (a) => isSome(getOption(a))
}
exports.getRefinement = getRefinement
/**
 * Returns an `A` value if possible
 */
function getRight(ma) {
  return ma._tag === "Left" ? exports.none : some(ma.right)
}
exports.getRight = getRight
/**
 * Returns `true` if the option is `None`, `false` otherwise
 *
 * @example
 * assert.strictEqual(isNone(some(1)), false)
 * assert.strictEqual(isNone(none), true)
 */
function isNone(fa) {
  return fa._tag === "None"
}
exports.isNone = isNone
/**
 * Returns `true` if the option is an instance of `Some`, `false` otherwise
 *
 * @example
 * assert.strictEqual(isSome(some(1)), true)
 * assert.strictEqual(isSome(none), false)
 */
function isSome(fa) {
  return fa._tag === "Some"
}
exports.isSome = isSome
/**
 * Use `A => B` to transform `Option[A]` to `Option[B]`
 */
const map_ = (ma, f) => (isNone(ma) ? exports.none : some(f(ma.value)))
exports.map_ = map_
/**
 * Use `A => B` to transform `Option[A]` to `Option[B]`
 */
const map = (f) => (fa) => (0, exports.map_)(fa, f)
exports.map = map
/**
 * This is `chain` + `fromNullable`, useful when working with optional values
 *
 * @example
 * interface Employee {
 *   company?: {
 *     address?: {
 *       street?: {
 *         name?: string
 *       }
 *     }
 *   }
 * }
 *
 * const employee1: Employee = { company: { address: { street: { name: 'high street' } } } }
 *
 * assert.deepStrictEqual(
 *   pipe(
 *     fromNullable(employee1.company),
 *     mapNullable(company => company.address),
 *     mapNullable(address => address.street),
 *     mapNullable(street => street.name)
 *   ),
 *   some('high street')
 * )
 *
 * const employee2: Employee = { company: { address: { street: {} } } }
 *
 * assert.deepStrictEqual(
 *   pipe(
 *     fromNullable(employee2.company),
 *     mapNullable(company => company.address),
 *     mapNullable(address => address.street),
 *     mapNullable(street => street.name)
 *   ),
 *   none
 * )
 */
function mapNullable(f) {
  return (ma) => (isNone(ma) ? exports.none : fromNullable(f(ma.value)))
}
exports.mapNullable = mapNullable
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns `null`.
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     toNullable
 *   ),
 *   1
 * )
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     toNullable
 *   ),
 *   null
 * )
 */
function toNullable(ma) {
  return isNone(ma) ? null : ma.value
}
exports.toNullable = toNullable
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns `undefined`.
 *
 * @example
 * assert.strictEqual(
 *   pipe(
 *     some(1),
 *     toUndefined
 *   ),
 *   1
 * )
 * assert.strictEqual(
 *   pipe(
 *     none,
 *     toUndefined
 *   ),
 *   undefined
 * )
 */
function toUndefined(ma) {
  return isNone(ma) ? undefined : ma.value
}
exports.toUndefined = toUndefined
/**
 * Transforms an exception into an `Option`. If `f` throws, returns `None`, otherwise returns the output wrapped in
 * `Some`
 *
 * @example
 * assert.deepStrictEqual(
 *   tryCatch(() => {
 *     throw new Error()
 *   }),
 *   none
 * )
 * assert.deepStrictEqual(tryCatch(() => 1), some(1))
 */
function tryCatch(f) {
  try {
    return some(f())
  } catch (e) {
    return exports.none
  }
}
exports.tryCatch = tryCatch
exports.PartialExceptionTypeId = Symbol()
class PartialException {
  constructor() {
    this._typeId = exports.PartialExceptionTypeId
  }
}
exports.PartialException = PartialException
function raisePartial() {
  throw new PartialException()
}
/**
 * Simulates a partial function
 */
function partial(f) {
  return (...args) => {
    try {
      return some(f(raisePartial)(...args))
    } catch (e) {
      if (e instanceof PartialException) {
        return exports.none
      }
      throw e
    }
  }
}
exports.partial = partial
//# sourceMappingURL=core.js.map
