"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.zipWithPar_ = exports.zipWithPar = exports.zipWith_ = exports.zipWith = exports.zip = exports.zip_ = exports.useForever = exports.useNow = exports.tap = exports.tap_ = exports.reserve = exports.makeReservation = exports.makeReservation_ = exports.Reservation = exports.provideAll_ = exports.provideAll = exports.provideSome = exports.provideSome_ = exports.onExitFirst_ = exports.onExitFirst = exports.onExit = exports.onExit_ = exports.mapM = exports.mapM_ = exports.map_ = exports.map = exports.makeReserve = exports.makeManagedReleaseMap = exports.makeInterruptible_ = exports.makeInterruptible = exports.make_ = exports.make = exports.foldCauseM_ = exports.foldCauseM = exports.finalizerRef = exports.failWith = exports.fail = exports.ensuring = exports.ensuring_ = exports.succeedWith = exports.chain_ = exports.chain = void 0;
const Tp = __importStar(require("../Collections/Immutable/Tuple"));
const ExecutionStrategy_1 = require("../Effect/ExecutionStrategy");
const Function_1 = require("../Function");
const Ref_1 = require("../Ref");
const T = __importStar(require("./deps-core"));
const fromEffect_1 = require("./fromEffect");
const makeExit_1 = require("./makeExit");
const managed_1 = require("./managed");
const add = __importStar(require("./ReleaseMap/add"));
const addIfOpen = __importStar(require("./ReleaseMap/addIfOpen"));
const makeReleaseMap = __importStar(require("./ReleaseMap/makeReleaseMap"));
const release = __importStar(require("./ReleaseMap/release"));
const releaseAll = __importStar(require("./ReleaseMap/releaseAll"));
const use_1 = require("./use");
/**
 * Returns a managed that models the execution of this managed, followed by
 * the passing of its value to the specified continuation function `f`,
 * followed by the managed that it returns.
 *
 * @ets_data_first chain_
 */
function chain(f, __trace) {
    return (self) => chain_(self, f, __trace);
}
exports.chain = chain;
/**
 * Returns a managed that models the execution of this managed, followed by
 * the passing of its value to the specified continuation function `f`,
 * followed by the managed that it returns.
 */
function chain_(self, f, __trace) {
    return (0, managed_1.managedApply)(T.chain_(self.effect, ({ tuple: [releaseSelf, a] }) => T.map_(f(a).effect, ({ tuple: [releaseThat, b] }) => Tp.tuple((e) => T.chain_(T.result(releaseThat(e)), (e1) => T.chain_(T.result(releaseSelf(e)), (e2) => T.done(T.exitZipRight_(e1, e2), __trace))), b), __trace)));
}
exports.chain_ = chain_;
/**
 * Imports a synchronous side-effect into a pure value
 */
function succeedWith(effect, __trace) {
    return (0, fromEffect_1.fromEffect)(T.succeedWith(effect, __trace));
}
exports.succeedWith = succeedWith;
/**
 * Ensures that `f` is executed when this Managed is finalized, after
 * the existing finalizer.
 *
 * For usecases that need access to the Managed's result, see `onExit`.
 */
function ensuring_(self, f, __trace) {
    return onExit_(self, () => f, __trace);
}
exports.ensuring_ = ensuring_;
/**
 * Ensures that `f` is executed when this Managed is finalized, after
 * the existing finalizer.
 *
 * For usecases that need access to the Managed's result, see `onExit`.
 *
 * @ets_data_first ensuring_
 */
function ensuring(f, __trace) {
    return (self) => ensuring_(self, f, __trace);
}
exports.ensuring = ensuring;
/**
 * Returns an effect that models failure with the specified error. The moral equivalent of throw for pure code.
 */
function fail(e, __trace) {
    return (0, fromEffect_1.fromEffect)(T.fail(e, __trace));
}
exports.fail = fail;
/**
 * Returns an effect that models failure with the specified error. The moral equivalent of throw for pure code.
 */
function failWith(e, __trace) {
    return (0, fromEffect_1.fromEffect)(T.failWith(e, __trace));
}
exports.failWith = failWith;
/**
 * Creates an effect that executes a finalizer stored in a `Ref`.
 * The `Ref` is yielded as the result of the effect, allowing for
 * control flows that require mutating finalizers.
 */
function finalizerRef(initial, __trace) {
    return (0, makeExit_1.makeExit_)((0, Ref_1.makeRef)(initial), (ref, exit) => T.chain_(ref.get, (f) => f(exit)), __trace);
}
exports.finalizerRef = finalizerRef;
/**
 * A more powerful version of `foldM` that allows recovering from any kind of failure except interruptions.
 *
 * @ets_data_first foldCauseM_
 */
function foldCauseM(f, g, __trace) {
    return (self) => foldCauseM_(self, f, g, __trace);
}
exports.foldCauseM = foldCauseM;
/**
 * A more powerful version of `foldM` that allows recovering from any kind of failure except interruptions.
 */
function foldCauseM_(self, f, g, __trace) {
    return (0, managed_1.managedApply)((T.foldCauseM_(self.effect, (c) => f(c).effect, ({ tuple: [_, a] }) => g(a).effect, __trace)));
}
exports.foldCauseM_ = foldCauseM_;
/**
 * Lifts a `Effect< R, E, A>` into `Managed< R, E, A>` with a release action.
 * The acquire and release actions will be performed uninterruptibly.
 *
 * @ets_data_first make_
 */
function make(release, __trace) {
    return (acquire) => make_(acquire, release, __trace);
}
exports.make = make;
/**
 * Lifts a `Effect< R, E, A>` into `Managed< R, E, A>` with a release action.
 * The acquire and release actions will be performed uninterruptibly.
 */
function make_(acquire, release, __trace) {
    return (0, makeExit_1.makeExit_)(acquire, release, __trace);
}
exports.make_ = make_;
/**
 * Lifts a `Effect< R, E, A>` into `Managed< R, E, A>` with a release action.
 * The acquire action will be performed interruptibly, while release
 * will be performed uninterruptibly.
 *
 * @ets_data_first makeInterruptible_
 */
function makeInterruptible(release, __trace) {
    return (acquire) => makeInterruptible_(acquire, release, __trace);
}
exports.makeInterruptible = makeInterruptible;
/**
 * Lifts a `Effect< R, E, A>` into `Managed< R, E, A>` with a release action.
 * The acquire action will be performed interruptibly, while release
 * will be performed uninterruptibly.
 */
function makeInterruptible_(acquire, release, __trace) {
    return onExitFirst_((0, fromEffect_1.fromEffect)(acquire, __trace), T.exitForeach(release), __trace);
}
exports.makeInterruptible_ = makeInterruptible_;
/**
 * Construct a `ReleaseMap` wrapped in a `Managed`. The `ReleaseMap` will
 * be released with the specified `ExecutionStrategy` as the release action
 * for the resulting `Managed`.
 */
function makeManagedReleaseMap(es, __trace) {
    return (0, makeExit_1.makeExit_)(makeReleaseMap.makeReleaseMap, (rm, e) => releaseAll.releaseAll(e, es)(rm), __trace);
}
exports.makeManagedReleaseMap = makeManagedReleaseMap;
/**
 * Creates a `Managed` from a `Reservation` produced by an effect. Evaluating
 * the effect that produces the reservation will be performed *uninterruptibly*,
 * while the acquisition step of the reservation will be performed *interruptibly*.
 * The release step will be performed uninterruptibly as usual.
 *
 * This two-phase acquisition allows for resource acquisition flows that can be
 * safely interrupted and released.
 */
function makeReserve(reservation, __trace) {
    return (0, managed_1.managedApply)(T.uninterruptibleMask(({ restore }) => (T.map_(T.bind_(T.bind_(T.bind_(T.let_(T.let_(T.bind_(T.do, "tp", () => T.environment()), "r", (s) => s.tp.get(0)), "releaseMap", (s) => s.tp.get(1)), "reserved", (s) => T.provideAll_(reservation, s.r)), "releaseKey", (s) => addIfOpen.addIfOpen((x) => T.provideAll_(s.reserved.release(x), s.r, __trace))(s.releaseMap)), "finalizerAndA", (s) => {
        const k = s.releaseKey;
        switch (k._tag) {
            case "None": {
                return T.interrupt;
            }
            case "Some": {
                return T.map_(restore(T.provideSome_(s.reserved.acquire, ({ tuple: [r] }) => r, __trace)), (a) => Tp.tuple((e) => release.release(k.value, e)(s.releaseMap), a));
            }
        }
    }), (s) => s.finalizerAndA))));
}
exports.makeReserve = makeReserve;
/**
 * Returns a managed whose success is mapped by the specified `f` function.
 *
 * @ets_data_first map_
 */
function map(f, __trace) {
    return (self) => map_(self, f, __trace);
}
exports.map = map;
/**
 * Returns a managed whose success is mapped by the specified `f` function.
 */
function map_(self, f, __trace) {
    return (0, managed_1.managedApply)(T.map_(self.effect, ({ tuple: [fin, a] }) => Tp.tuple(fin, f(a)), __trace));
}
exports.map_ = map_;
/**
 * Returns a managed whose success is mapped by the specified `f` function.
 */
function mapM_(self, f, __trace) {
    return (0, managed_1.managedApply)(T.chain_(self.effect, ({ tuple: [fin, a] }) => T.provideSome_(T.map_(f(a), (b) => Tp.tuple(fin, b), __trace), ({ tuple: [r] }) => r)));
}
exports.mapM_ = mapM_;
/**
 * Returns a managed whose success is mapped by the specified `f` function.
 */
function mapM(f, __trace) {
    return (self) => mapM_(self, f, __trace);
}
exports.mapM = mapM;
/**
 * Ensures that a cleanup function runs when this Managed is finalized, after
 * the existing finalizers.
 */
function onExit_(self, cleanup, __trace) {
    return (0, managed_1.managedApply)(T.uninterruptibleMask(({ restore }) => (T.map_(T.bind_(T.bind_(T.bind_(T.bind_(T.let_(T.let_(T.bind_(T.do, "tp", () => T.environment()), "r", (s) => s.tp.get(0)), "outerReleaseMap", (s) => s.tp.get(1)), "innerReleaseMap", () => makeReleaseMap.makeReleaseMap), "exitEA", (s) => T.provideAll_(T.result(restore(T.map_(self.effect, ({ tuple: [_, a] }) => a))), Tp.tuple(s.r, s.innerReleaseMap))), "releaseMapEntry", (s) => add.add((e) => (T.flatten(T.zipWith_(T.result(releaseAll.releaseAll(e, ExecutionStrategy_1.sequential)(s.innerReleaseMap)), (T.result(T.provideAll_(cleanup(s.exitEA), s.r))), (l, r) => T.done(T.exitZipRight_(l, r)), __trace))))(s.outerReleaseMap)), "a", (s) => T.done(s.exitEA)), (s) => Tp.tuple(s.releaseMapEntry, s.a)))));
}
exports.onExit_ = onExit_;
/**
 * Ensures that a cleanup function runs when this Managed is finalized, after
 * the existing finalizers.
 *
 * @ets_data_first onExit_
 */
function onExit(cleanup, __trace) {
    return (self) => onExit_(self, cleanup, __trace);
}
exports.onExit = onExit;
/**
 * Ensures that a cleanup function runs when this Managed is finalized, before
 * the existing finalizers.
 *
 * @ets_data_first onExitFirst_
 */
function onExitFirst(cleanup, __trace) {
    return (self) => onExitFirst_(self, cleanup, __trace);
}
exports.onExitFirst = onExitFirst;
/**
 * Ensures that a cleanup function runs when this Managed is finalized, before
 * the existing finalizers.
 */
function onExitFirst_(self, cleanup, __trace) {
    return (0, managed_1.managedApply)(T.uninterruptibleMask(({ restore }) => (T.map_(T.bind_(T.bind_(T.bind_(T.bind_(T.let_(T.let_(T.bind_(T.do, "tp", () => T.environment()), "r", (s) => s.tp.get(0)), "outerReleaseMap", (s) => s.tp.get(1)), "innerReleaseMap", () => makeReleaseMap.makeReleaseMap), "exitEA", (s) => T.provideAll_(T.result(restore(T.map_(self.effect, ({ tuple: [_, a] }) => a))), Tp.tuple(s.r, s.innerReleaseMap))), "releaseMapEntry", (s) => add.add((e) => T.flatten(T.zipWith_(T.result(T.provideAll_(cleanup(s.exitEA), s.r, __trace)), T.result(releaseAll.releaseAll(e, ExecutionStrategy_1.sequential)(s.innerReleaseMap)), (l, r) => T.done(T.exitZipRight_(l, r)))))(s.outerReleaseMap)), "a", (s) => T.done(s.exitEA)), (s) => Tp.tuple(s.releaseMapEntry, s.a)))));
}
exports.onExitFirst_ = onExitFirst_;
/**
 * Like provideSome_ for effect but for Managed
 */
function provideSome_(self, f, __trace) {
    return (0, managed_1.managedApply)(T.accessM(({ tuple: [r0, rm] }) => T.provideAll_(self.effect, Tp.tuple(f(r0), rm), __trace)));
}
exports.provideSome_ = provideSome_;
/**
 * Like provideSome for effect but for Managed
 *
 * @ets_data_first provideSome_
 */
function provideSome(f, __trace) {
    return (self) => provideSome_(self, f, __trace);
}
exports.provideSome = provideSome;
/**
 * Provides the `Managed` effect with its required environment, which eliminates
 * its dependency on `R`.
 *
 * @ets_data_first provideAll_
 */
function provideAll(r, __trace) {
    return (self) => provideAll_(self, r);
}
exports.provideAll = provideAll;
/**
 * Provides the `Managed` effect with its required environment, which eliminates
 * its dependency on `R`.
 */
function provideAll_(self, r, __trace) {
    return provideSome_(self, () => r, __trace);
}
exports.provideAll_ = provideAll_;
/**
 * A `Reservation<R, E, A>` encapsulates resource acquisition and disposal
 * without specifying when or how that resource might be used.
 *
 * See `Managed#reserve` and `Effect#reserve` for details of usage.
 */
class Reservation {
    constructor(acquire, release) {
        this.acquire = acquire;
        this.release = release;
    }
}
exports.Reservation = Reservation;
Reservation.of = (acquire, release) => new Reservation(acquire, release);
/**
 * Make a new reservation
 */
function makeReservation_(acquire, release) {
    return Reservation.of(acquire, release);
}
exports.makeReservation_ = makeReservation_;
/**
 * Make a new reservation
 *
 * @ets_data_first makeReservation_
 */
function makeReservation(release) {
    return (acquire) => Reservation.of(acquire, release);
}
exports.makeReservation = makeReservation;
/**
 * Lifts a pure `Reservation< R, E, A>` into `Managed< R, E, A>`. The acquisition step
 * is performed interruptibly.
 */
function reserve(reservation, __trace) {
    return makeReserve(T.succeed(reservation), __trace);
}
exports.reserve = reserve;
/**
 * Returns a managed that effectfully peeks at the acquired resource.
 */
function tap_(self, f, __trace) {
    return chain_(self, (a) => map_(f(a), () => a), __trace);
}
exports.tap_ = tap_;
/**
 * Returns a managed that effectfully peeks at the acquired resource.
 *
 * @ets_data_first tap_
 */
function tap(f, __trace) {
    return (self) => tap_(self, f, __trace);
}
exports.tap = tap;
/**
 * Runs the acquire and release actions and returns the result of this
 * managed effect. Note that this is only safe if the result of this managed
 * effect is valid outside its scope.
 */
function useNow(self, __trace) {
    return (0, use_1.use_)(self, T.succeed, __trace);
}
exports.useNow = useNow;
/**
 * Use the resource until interruption. Useful for resources that you want
 * to acquire and use as long as the application is running, like a
 * HTTP server.
 */
function useForever(self, __trace) {
    return (0, use_1.use_)(self, () => T.never, __trace);
}
exports.useForever = useForever;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in sequence, combining their results with the specified `f` function.
 */
function zip_(self, that, __trace) {
    return zipWith_(self, that, (a, a2) => [a, a2], __trace);
}
exports.zip_ = zip_;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in sequence, combining their results with the specified `f` function.
 *
 * @ets_data_first zip_
 */
function zip(that, __trace) {
    return (self) => zip_(self, that, __trace);
}
exports.zip = zip;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in sequence, combining their results with the specified `f` function.
 *
 * @ets_data_first zipWith_
 */
function zipWith(that, f, __trace) {
    return (self) => zipWith_(self, that, f, __trace);
}
exports.zipWith = zipWith;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in sequence, combining their results with the specified `f` function.
 */
function zipWith_(self, that, f, __trace) {
    return chain_(self, (a) => map_(that, (a2) => f(a, a2)), __trace);
}
exports.zipWith_ = zipWith_;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in parallel, combining their results with the specified `f` function.
 *
 * @ets_data_first zipWithPar_
 */
function zipWithPar(that, f, __trace) {
    return (self) => zipWithPar_(self, that, f, __trace);
}
exports.zipWithPar = zipWithPar;
/**
 * Returns a managed that executes both this managed and the specified managed,
 * in parallel, combining their results with the specified `f` function.
 */
function zipWithPar_(self, that, f, __trace) {
    return mapM_(makeManagedReleaseMap(ExecutionStrategy_1.parallel), (parallelReleaseMap) => {
        const innerMap = T.provideSome_(makeManagedReleaseMap(ExecutionStrategy_1.sequential).effect, (r) => Tp.tuple(r, parallelReleaseMap));
        return T.chain_(T.zip_(innerMap, innerMap, __trace), ({ tuple: [{ tuple: [_, l] }, { tuple: [__, r] }] }) => T.zipWithPar_(T.provideSome_(self.effect, (_) => Tp.tuple(_, l)), T.provideSome_(that.effect, (_) => Tp.tuple(_, r)), ({ tuple: [_, a] }, { tuple: [__, a2] }) => f(a, a2), __trace));
    });
}
exports.zipWithPar_ = zipWithPar_;
//# sourceMappingURL=core.js.map