"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.contramap_ = exports.contramapM = exports.contramapM_ = exports.mapM = exports.mapM_ = exports.filterOutput = exports.filterOutput_ = exports.filterOutputM = exports.filterOutputM_ = exports.filterInput = exports.filterInput_ = exports.filterInputM = exports.filterInputM_ = exports.dimapError = exports.dimapError_ = exports.dimapM = exports.dimapM_ = exports.collect = exports.collect_ = exports.collectM = exports.collectM_ = exports.foldAllM = exports.foldAllM_ = exports.foldM = exports.foldM_ = exports.fold = exports.fold_ = exports.updateSomeAndGet = exports.updateSomeAndGet_ = exports.updateSome = exports.updateSome_ = exports.updateAndGet = exports.updateAndGet_ = exports.update = exports.update_ = exports.modifySome = exports.modifySome_ = exports.getAndUpdateSome = exports.getAndUpdateSome_ = exports.getAndUpdate = exports.getAndUpdate_ = exports.getAndSet = exports.getAndSet_ = exports.get = exports.modify = exports.modify_ = exports.dequeueRef = exports.makeManagedRefM = exports.unsafeMakeRefM = exports.makeRefM = void 0;
exports.tapOutput = exports.tapOutput_ = exports.tapInput = exports.tapInput_ = exports.writeOnly = exports.readOnly = exports.map = exports.map_ = exports.contramap = void 0;
// ets_tracing: off
const Tp = __importStar(require("../Collections/Immutable/Tuple"));
const E = __importStar(require("../Either"));
const Function_1 = require("../Function");
const O = __importStar(require("../Option"));
const Q = __importStar(require("../Queue"));
const R = __importStar(require("../Ref"));
const S = __importStar(require("../Semaphore"));
const Utils_1 = require("../Utils");
const T = __importStar(require("./effect"));
const XRefM_1 = require("./XRefM");
/**
 * Creates a new `XRefM` with the specified value.
 */
function makeRefM(a) {
    return (T.map_(T.bind_(T.bind_(T.do, "ref", () => R.makeRef(a)), "semaphore", () => S.makeSemaphore(1)), ({ ref, semaphore }) => new XRefM_1.AtomicM(ref, semaphore)));
}
exports.makeRefM = makeRefM;
/**
 * Creates a new `XRefM` with the specified value.
 */
function unsafeMakeRefM(a) {
    const ref = R.unsafeMakeRef(a);
    const semaphore = S.unsafeMakeSemaphore(1);
    return new XRefM_1.AtomicM(ref, semaphore);
}
exports.unsafeMakeRefM = unsafeMakeRefM;
/**
 * Creates a new `RefM` with the specified value in the context of a
 * `Managed.`
 */
function makeManagedRefM(a) {
    return (T.toManaged(makeRefM(a)));
}
exports.makeManagedRefM = makeManagedRefM;
/**
 * Creates a new `RefM` and a `Dequeue` that will emit every change to the
 * `RefM`.
 */
function dequeueRef(a) {
    return (T.map_(T.bind_(T.bind_(T.do, "ref", () => makeRefM(a)), "queue", () => Q.makeUnbounded()), ({ queue, ref }) => [tapInput_(ref, (a) => Q.offer_(queue, a)), queue]));
}
exports.dequeueRef = dequeueRef;
/**
 * Atomically modifies the `RefM` with the specified function, which computes
 * a return value for the modification. This is a more powerful version of
 * `update`.
 */
function modify_(self, f) {
    return ((0, Utils_1.matchTag)({
        AtomicM: (atomic) => (S.withPermit_(T.chain_(T.chain_(atomic.ref.get, f), ({ tuple: [b, a] }) => (T.as_(atomic.ref.set(a), b))), atomic.semaphore)),
        DerivedM: (derived) => derived.use((value, getEither, setEither) => (S.withPermit_(T.chain_(value.ref.get, (a) => (T.chain_(T.chain_(getEither(a), f), ({ tuple: [b, a] }) => (T.as_(T.chain_(setEither(a), (a) => value.ref.set(a)), b))))), value.semaphore))),
        DerivedAllM: (derivedAll) => derivedAll.use((value, getEither, setEither) => (S.withPermit_(T.chain_(value.ref.get, (s) => (T.chain_(T.chain_(getEither(s), f), ({ tuple: [b, a] }) => (T.as_(T.chain_(setEither(a)(s), (a) => value.ref.set(a)), b))))), value.semaphore)))
    })((0, XRefM_1.concrete)(self)));
}
exports.modify_ = modify_;
/**
 * Atomically modifies the `RefM` with the specified function, which computes
 * a return value for the modification. This is a more powerful version of
 * `update`.
 */
function modify(f) {
    return (self) => modify_(self, f);
}
exports.modify = modify;
/**
 * Reads the value from the `XRefM`.
 */
function get(self) {
    return self.get;
}
exports.get = get;
/**
 * Writes a new value to the `RefM`, returning the value immediately before
 * modification.
 */
function getAndSet_(self, a) {
    return modify_(self, (v) => T.succeed(Tp.tuple(v, a)));
}
exports.getAndSet_ = getAndSet_;
/**
 * Writes a new value to the `RefM`, returning the value immediately before
 * modification.
 */
function getAndSet(a) {
    return (self) => getAndSet_(self, a);
}
exports.getAndSet = getAndSet;
/**
 * Atomically modifies the `RefM` with the specified function, returning the
 * value immediately before modification.
 */
function getAndUpdate_(self, f) {
    return modify_(self, (v) => T.map_(f(v), (r) => Tp.tuple(v, r)));
}
exports.getAndUpdate_ = getAndUpdate_;
/**
 * Atomically modifies the `RefM` with the specified function, returning the
 * value immediately before modification.
 */
function getAndUpdate(f) {
    return (self) => getAndUpdate_(self, f);
}
exports.getAndUpdate = getAndUpdate;
/**
 * Atomically modifies the `RefM` with the specified function, returning the
 * value immediately before modification.
 */
function getAndUpdateSome_(self, f) {
    return modify_(self, (v) => (T.map_(O.getOrElse_(f(v), () => T.succeed(v)), (r) => Tp.tuple(v, r))));
}
exports.getAndUpdateSome_ = getAndUpdateSome_;
/**
 * Atomically modifies the `RefM` with the specified function, returning the
 * value immediately before modification.
 */
function getAndUpdateSome(f) {
    return (self) => getAndUpdateSome_(self, f);
}
exports.getAndUpdateSome = getAndUpdateSome;
/**
 * Atomically modifies the `RefM` with the specified function, which computes
 * a return value for the modification if the function is defined in the current value
 * otherwise it returns a default value.
 * This is a more powerful version of `updateSome`.
 */
function modifySome_(self, def, f) {
    return modify_(self, (v) => O.getOrElse_(f(v), () => T.succeed(Tp.tuple(def, v))));
}
exports.modifySome_ = modifySome_;
/**
 * Atomically modifies the `RefM` with the specified function, which computes
 * a return value for the modification if the function is defined in the current value
 * otherwise it returns a default value.
 * This is a more powerful version of `updateSome`.
 */
function modifySome(def) {
    return (f) => (self) => modifySome_(self, def, f);
}
exports.modifySome = modifySome;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function update_(self, f) {
    return modify_(self, (v) => T.map_(f(v), (r) => Tp.tuple(undefined, r)));
}
exports.update_ = update_;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function update(f) {
    return (self) => update_(self, f);
}
exports.update = update;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateAndGet_(self, f) {
    return modify_(self, (v) => (T.map_(f(v), (r) => Tp.tuple(r, r))));
}
exports.updateAndGet_ = updateAndGet_;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateAndGet(f) {
    return (self) => updateAndGet_(self, f);
}
exports.updateAndGet = updateAndGet;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateSome_(self, f) {
    return modify_(self, (v) => (T.map_(O.getOrElse_(f(v), () => T.succeed(v)), (r) => Tp.tuple(undefined, r))));
}
exports.updateSome_ = updateSome_;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateSome(f) {
    return (self) => updateSome_(self, f);
}
exports.updateSome = updateSome;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateSomeAndGet_(self, f) {
    return modify_(self, (v) => (T.map_(O.getOrElse_(f(v), () => T.succeed(v)), (r) => Tp.tuple(r, r))));
}
exports.updateSomeAndGet_ = updateSomeAndGet_;
/**
 * Atomically modifies the `RefM` with the specified function.
 */
function updateSomeAndGet(f) {
    return (self) => updateSomeAndGet_(self, f);
}
exports.updateSomeAndGet = updateSomeAndGet;
/**
 * Folds over the error and value types of the `XRefM`.
 */
function fold_(self, ea, eb, ca, bd) {
    return self.foldM(ea, eb, (c) => T.fromEither(() => ca(c)), (b) => T.fromEither(() => bd(b)));
}
exports.fold_ = fold_;
/**
 * Folds over the error and value types of the `XRefM`.
 */
function fold(ea, eb, ca, bd) {
    return (self) => self.foldM(ea, eb, (c) => T.fromEither(() => ca(c)), (b) => T.fromEither(() => bd(b)));
}
exports.fold = fold;
/**
 * Folds over the error and value types of the `XRefM`. This is a highly
 * polymorphic method that is capable of arbitrarily transforming the error
 * and value types of the `XRefM`. For most use cases one of the more
 * specific combinators implemented in terms of `foldM` will be more
 * ergonomic but this method is extremely useful for implementing new
 * combinators.
 */
function foldM_(self, ea, eb, ca, bd) {
    return self.foldM(ea, eb, ca, bd);
}
exports.foldM_ = foldM_;
/**
 * Folds over the error and value types of the `XRefM`. This is a highly
 * polymorphic method that is capable of arbitrarily transforming the error
 * and value types of the `XRefM`. For most use cases one of the more
 * specific combinators implemented in terms of `foldM` will be more
 * ergonomic but this method is extremely useful for implementing new
 * combinators.
 */
function foldM(ea, eb, ca, bd) {
    return (self) => self.foldM(ea, eb, ca, bd);
}
exports.foldM = foldM;
/**
 * Folds over the error and value types of the `XRefM`, allowing access to
 * the state in transforming the `set` value. This is a more powerful version
 * of `foldM` but requires unifying the environment and error types.
 */
function foldAllM_(self, ea, eb, ec, ca, bd) {
    return self.foldAllM(ea, eb, ec, ca, bd);
}
exports.foldAllM_ = foldAllM_;
/**
 * Folds over the error and value types of the `XRefM`, allowing access to
 * the state in transforming the `set` value. This is a more powerful version
 * of `foldM` but requires unifying the environment and error types.
 */
function foldAllM(ea, eb, ec, ca, bd) {
    return (self) => self.foldAllM(ea, eb, ec, ca, bd);
}
exports.foldAllM = foldAllM;
/**
 * Maps and filters the `get` value of the `XRefM` with the specified
 * effectual partial function, returning a `XRefM` with a `get` value that
 * succeeds with the result of the partial function if it is defined or else
 * fails with `None`.
 */
function collectM_(self, f) {
    return self.foldM(Function_1.identity, (_) => O.some(_), (_) => T.succeed(_), (b) => (O.getOrElse_(O.map_(f(b), (a) => T.asSomeError(a)), () => T.fail(O.none))));
}
exports.collectM_ = collectM_;
/**
 * Maps and filters the `get` value of the `XRefM` with the specified
 * effectual partial function, returning a `XRefM` with a `get` value that
 * succeeds with the result of the partial function if it is defined or else
 * fails with `None`.
 */
function collectM(f) {
    return (self) => collectM_(self, f);
}
exports.collectM = collectM;
/**
 * Maps and filters the `get` value of the `XRefM` with the specified partial
 * function, returning a `XRefM` with a `get` value that succeeds with the
 * result of the partial function if it is defined or else fails with `None`.
 */
function collect_(self, f) {
    return collectM_(self, (b) => (O.map_(f(b), T.succeed)));
}
exports.collect_ = collect_;
/**
 * Maps and filters the `get` value of the `XRefM` with the specified partial
 * function, returning a `XRefM` with a `get` value that succeeds with the
 * result of the partial function if it is defined or else fails with `None`.
 */
function collect(f) {
    return (self) => collect_(self, f);
}
exports.collect = collect;
/**
 * Transforms both the `set` and `get` values of the `XRefM` with the
 * specified effectual functions.
 */
function dimapM_(self, f, g) {
    return self.foldM((ea) => ea, (eb) => eb, f, g);
}
exports.dimapM_ = dimapM_;
/**
 * Transforms both the `set` and `get` values of the `XRefM` with the
 * specified effectual functions.
 */
function dimapM(f, g) {
    return (self) => dimapM_(self, f, g);
}
exports.dimapM = dimapM;
/**
 * Transforms both the `set` and `get` errors of the `XRefM` with the
 * specified functions.
 */
function dimapError_(self, f, g) {
    return fold_(self, (ea) => f(ea), (eb) => g(eb), (a) => E.right(a), (b) => E.right(b));
}
exports.dimapError_ = dimapError_;
/**
 * Transforms both the `set` and `get` errors of the `XRefM` with the
 * specified functions.
 */
function dimapError(f, g) {
    return (self) => dimapError_(self, f, g);
}
exports.dimapError = dimapError;
/**
 * Filters the `set` value of the `XRefM` with the specified effectual
 * predicate, returning a `XRefM` with a `set` value that succeeds if the
 * predicate is satisfied or else fails with `None`.
 */
function filterInputM_(self, f) {
    return foldM_(self, (ea) => O.some(ea), Function_1.identity, (a) => T.ifM_(T.asSomeError(f(a)), () => T.succeed(a), () => T.fail(O.none)), T.succeed);
}
exports.filterInputM_ = filterInputM_;
/**
 * Filters the `set` value of the `XRefM` with the specified effectual
 * predicate, returning a `XRefM` with a `set` value that succeeds if the
 * predicate is satisfied or else fails with `None`.
 */
function filterInputM(f) {
    return (self) => filterInputM_(self, f);
}
exports.filterInputM = filterInputM;
/**
 * Filters the `set` value of the `XRefM` with the specified effectual
 * predicate, returning a `XRefM` with a `set` value that succeeds if the
 * predicate is satisfied or else fails with `None`.
 */
function filterInput_(self, f) {
    return filterInputM_(self, (a) => T.succeed(f(a)));
}
exports.filterInput_ = filterInput_;
/**
 * Filters the `set` value of the `XRefM` with the specified effectual
 * predicate, returning a `XRefM` with a `set` value that succeeds if the
 * predicate is satisfied or else fails with `None`.
 */
function filterInput(f) {
    return (self) => filterInput_(self, f);
}
exports.filterInput = filterInput;
/**
 * Filters the `get` value of the `XRefM` with the specified effectual predicate,
 * returning a `XRefM` with a `get` value that succeeds if the predicate is
 * satisfied or else fails with `None`.
 */
function filterOutputM_(self, f) {
    return foldM_(self, (ea) => ea, (eb) => O.some(eb), (a) => T.succeed(a), (b) => T.ifM_(T.asSomeError(f(b)), () => T.succeed(b), () => T.fail(O.none)));
}
exports.filterOutputM_ = filterOutputM_;
/**
 * Filters the `get` value of the `XRefM` with the specified effectual predicate,
 * returning a `XRefM` with a `get` value that succeeds if the predicate is
 * satisfied or else fails with `None`.
 */
function filterOutputM(f) {
    return (self) => filterOutputM_(self, f);
}
exports.filterOutputM = filterOutputM;
/**
 * Filters the `get` value of the `XRefM` with the specified predicate,
 * returning a `XRefM` with a `get` value that succeeds if the predicate is
 * satisfied or else fails with `None`.
 */
function filterOutput_(self, f) {
    return filterOutputM_(self, (b) => T.succeed(f(b)));
}
exports.filterOutput_ = filterOutput_;
/**
 * Filters the `get` value of the `XRefM` with the specified predicate,
 * returning a `XRefM` with a `get` value that succeeds if the predicate is
 * satisfied or else fails with `None`.
 */
function filterOutput(f) {
    return (self) => filterOutput_(self, f);
}
exports.filterOutput = filterOutput;
/**
 * Transforms the `get` value of the `XRefM` with the specified effectual
 * function.
 */
function mapM_(self, f) {
    return dimapM_(self, T.succeed, f);
}
exports.mapM_ = mapM_;
/**
 * Transforms the `get` value of the `XRefM` with the specified effectual
 * function.
 */
function mapM(f) {
    return (self) => mapM_(self, f);
}
exports.mapM = mapM;
/**
 * Transforms the `set` value of the `XRefM` with the specified effectual
 * function.
 */
function contramapM_(self, f) {
    return dimapM_(self, f, T.succeed);
}
exports.contramapM_ = contramapM_;
/**
 * Transforms the `set` value of the `XRefM` with the specified effectual
 * function.
 */
function contramapM(f) {
    return (self) => contramapM_(self, f);
}
exports.contramapM = contramapM;
/**
 * Transforms the `set` value of the `XRefM` with the specified function.
 */
function contramap_(self, f) {
    return contramapM_(self, (c) => T.succeed(f(c)));
}
exports.contramap_ = contramap_;
/**
 * Transforms the `set` value of the `XRefM` with the specified function.
 */
function contramap(f) {
    return (self) => contramap_(self, f);
}
exports.contramap = contramap;
/**
 * Transforms the `get` value of the `XRefM` with the specified function.
 */
function map_(self, f) {
    return mapM_(self, (b) => T.succeed(f(b)));
}
exports.map_ = map_;
/**
 * Transforms the `get` value of the `XRefM` with the specified function.
 */
function map(f) {
    return (self) => map_(self, f);
}
exports.map = map;
/**
 * Returns a read only view of the `XRefM`.
 */
function readOnly(self) {
    return self;
}
exports.readOnly = readOnly;
/**
 * Returns a read only view of the `XRefM`.
 */
function writeOnly(self) {
    return fold_(self, Function_1.identity, () => undefined, E.right, () => E.left(undefined));
}
exports.writeOnly = writeOnly;
/**
 * Performs the specified effect every time a value is written to this
 * `XRefM`.
 */
function tapInput_(self, f) {
    return contramapM_(self, (c) => (T.as_(f(c), c)));
}
exports.tapInput_ = tapInput_;
/**
 * Performs the specified effect every time a value is written to this
 * `XRefM`.
 */
function tapInput(f) {
    return (self) => tapInput_(self, f);
}
exports.tapInput = tapInput;
/**
 * Performs the specified effect every time a value is read to this
 * `XRefM`.
 */
function tapOutput_(self, f) {
    return mapM_(self, (b) => (T.as_(f(b), b)));
}
exports.tapOutput_ = tapOutput_;
/**
 * Performs the specified effect every time a value is read to this
 * `XRefM`.
 */
function tapOutput(f) {
    return (self) => tapOutput_(self, f);
}
exports.tapOutput = tapOutput;
//# sourceMappingURL=api.js.map