"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.getOrElseS_ = exports.getOrElse_ = exports.getOrElseS = exports.getOrElse = exports.getLeft = exports.fromPredicate_ = exports.fromPredicate = exports.fromNullable = exports.fromEither = exports.fold_ = exports.fold = exports.extend_ = exports.extend = exports.exists_ = exports.exists = exports.duplicate = exports.flatten = exports.tap_ = exports.tap = exports.chain = exports.chain_ = exports.zipSecond_ = exports.zipSecond = exports.zipFirst_ = exports.zipFirst = exports.zip = exports.zip_ = exports.ap = exports.ap_ = exports.some = exports.emptyOf = exports.none = exports.Some = exports.None = void 0;
// ets_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
 */
function emptyOf() {
    return exports.none;
}
exports.emptyOf = emptyOf;
/**
 * Constructs Some(A)
 */
function some(a) {
    return new Some(a);
}
exports.some = some;
/**
 * Classic applicative
 */
function ap_(fab, fa) {
    return isNone(fab) ? exports.none : isNone(fa) ? exports.none : some(fab.value(fa.value));
}
exports.ap_ = ap_;
/**
 * Classic applicative
 *
 * @ets_data_first ap_
 */
function ap(fa) {
    return (fab) => ap_(fab, fa);
}
exports.ap = ap;
/**
 * Zips `Option[A]` and `Option[B]` into `Option[(A, B)]
 */
function zip_(fa, fb) {
    return chain_(fa, (a) => map_(fb, (b) => Tp.tuple(a, b)));
}
exports.zip_ = zip_;
/**
 * Zips `Option[A]` and `Option[B]` into `Option[(A, B)]
 *
 * @ets_data_first zip_
 */
function zip(fb) {
    return (fa) => zip_(fa, fb);
}
exports.zip = zip;
/**
 * Apply both and return first
 *
 * @ets_data_first zipFirst_
 */
function zipFirst(fb) {
    return (fa) => zipFirst_(fa, fb);
}
exports.zipFirst = zipFirst;
/**
 * Apply both and return first
 */
function zipFirst_(fa, fb) {
    return ap_(map_(fa, (a) => () => a), fb);
}
exports.zipFirst_ = zipFirst_;
/**
 * Apply both and return second
 *
 * @ets_data_first zipSecond_
 */
function zipSecond(fb) {
    return (fa) => zipSecond_(fa, fb);
}
exports.zipSecond = zipSecond;
/**
 * Apply both and return second
 */
function zipSecond_(fa, fb) {
    return ap_(map_(fa, () => (b) => b), fb);
}
exports.zipSecond_ = zipSecond_;
/**
 * Builds a new option constructed using the value of self
 */
function chain_(self, f) {
    return isNone(self) ? exports.none : f(self.value);
}
exports.chain_ = chain_;
/**
 * Builds a new option constructed using the value of self
 *
 * @ets_data_first chain_
 */
function chain(f) {
    return (self) => chain_(self, f);
}
exports.chain = chain;
/**
 * Like chain but ignores the constructed outout
 *
 * @ets_data_first tap_
 */
function tap(f) {
    return (ma) => chain_(ma, (a) => map_(f(a), () => a));
}
exports.tap = tap;
/**
 * Like chain but ignores the constructed outout
 */
function tap_(ma, f) {
    return chain_(ma, (a) => map_(f(a), () => a));
}
exports.tap_ = tap_;
/**
 * Flattens nested options
 */
function flatten(fa) {
    return chain_(fa, core_1.identity);
}
exports.flatten = flatten;
/**
 * Wraps this option into a second one
 */
function duplicate(ma) {
    return isNone(ma) ? exports.none : some(ma);
}
exports.duplicate = duplicate;
/**
 * Returns `true` if the predicate is satisfied by the wrapped value
 *
 * @ets_data_first exists_
 */
function exists(predicate) {
    return (ma) => (isNone(ma) ? false : predicate(ma.value));
}
exports.exists = exists;
/**
 * Returns `true` if the predicate is satisfied by the wrapped value
 */
function exists_(ma, predicate) {
    return isNone(ma) ? false : predicate(ma.value);
}
exports.exists_ = exists_;
/**
 * Apply `Option[A] => B` in case self is some returning `Option[B]`
 *
 * @ets_data_first extend_
 */
function extend(f) {
    return (self) => extend_(self, f);
}
exports.extend = extend;
/**
 * Apply `Option[A] => B` in case self is some returning `Option[B]`
 */
function extend_(self, f) {
    return 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.
 *
 * @ets_data_first fold_
 */
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.
 */
function fold_(ma, onNone, onSome) {
    return isNone(ma) ? onNone() : onSome(ma.value);
}
exports.fold_ = fold_;
/**
 * Constructs `Option[A]` from `Either[E, A]` discarding `E`
 */
function fromEither(ma) {
    return 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`
 */
function fromNullable(a) {
    return a == null ? exports.none : some(a);
}
exports.fromNullable = fromNullable;
/**
 * Returns a smart constructor based on the given predicate
 *
 * @ets_data_first fromPredicate_
 */
function fromPredicate(predicate) {
    return (a) => (predicate(a) ? some(a) : exports.none);
}
exports.fromPredicate = fromPredicate;
/**
 * Returns a smart constructor based on the given predicate
 */
function fromPredicate_(a, predicate) {
    return 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
 *
 * @ets_data_first getOrElse_
 */
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
 *
 * @ets_data_first getOrElseS_
 */
function getOrElseS(onNone) {
    return getOrElse(onNone);
}
exports.getOrElseS = getOrElseS;
/**
 * Extracts the value out of the structure, if it exists. Otherwise returns the given default value
 */
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 getOrElseS_(ma, onNone) {
    return getOrElse_(ma, onNone);
}
exports.getOrElseS_ = getOrElseS_;
/**
 * 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.
 */
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
 */
function isNone(fa) {
    return fa._tag === "None";
}
exports.isNone = isNone;
/**
 * Returns `true` if the option is an instance of `Some`, `false` otherwise
 */
function isSome(fa) {
    return fa._tag === "Some";
}
exports.isSome = isSome;
/**
 * Use `A => B` to transform `Option[A]` to `Option[B]`
 */
function map_(ma, f) {
    return isNone(ma) ? exports.none : some(f(ma.value));
}
exports.map_ = map_;
/**
 * Use `A => B` to transform `Option[A]` to `Option[B]`
 *
 * @ets_data_first map_
 */
function map(f) {
    return (fa) => map_(fa, f);
}
exports.map = map;
/**
 * This is `chain` + `fromNullable`, useful when working with optional values
 */
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`.
 */
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`.
 */
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`
 */
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