// ets_tracing: off import * as Tp from "../Collections/Immutable/Tuple/index.js" import * as E from "../Either/index.js" import { identity, pipe } from "../Function/index.js" import type * as M from "../Managed/managed.js" import * as O from "../Option/index.js" import * as Q from "../Queue/index.js" import * as R from "../Ref/index.js" import * as S from "../Semaphore/index.js" import { matchTag } from "../Utils/index.js" import * as T from "./effect.js" import type { RefM, XRefM } from "./XRefM.js" import { AtomicM, concrete } from "./XRefM.js" /** * Creates a new `XRefM` with the specified value. */ export function makeRefM(a: A): T.UIO> { return pipe( T.do, T.bind("ref", () => R.makeRef(a)), T.bind("semaphore", () => S.makeSemaphore(1)), T.map(({ ref, semaphore }) => new AtomicM(ref, semaphore)) ) } /** * Creates a new `XRefM` with the specified value. */ export function unsafeMakeRefM(a: A): RefM { const ref = R.unsafeMakeRef(a) const semaphore = S.unsafeMakeSemaphore(1) return new AtomicM(ref, semaphore) } /** * Creates a new `RefM` with the specified value in the context of a * `Managed.` */ export function makeManagedRefM(a: A): M.UIO> { return pipe(makeRefM(a), T.toManaged) } /** * Creates a new `RefM` and a `Dequeue` that will emit every change to the * `RefM`. */ export function dequeueRef(a: A): T.UIO<[RefM, Q.Dequeue]> { return pipe( T.do, T.bind("ref", () => makeRefM(a)), T.bind("queue", () => Q.makeUnbounded()), T.map(({ queue, ref }) => [tapInput_(ref, (a) => Q.offer_(queue, a)), queue]) ) } /** * Atomically modifies the `RefM` with the specified function, which computes * a return value for the modification. This is a more powerful version of * `update`. */ export function modify_( self: XRefM, f: (a: A) => T.Effect> ): T.Effect { return pipe( self, concrete, matchTag({ AtomicM: (atomic) => pipe( atomic.ref.get, T.chain(f), T.chain(({ tuple: [b, a] }) => pipe(atomic.ref.set(a), T.as(b))), S.withPermit(atomic.semaphore) ), DerivedM: (derived) => derived.use((value, getEither, setEither) => pipe( value.ref.get, T.chain((a) => pipe( getEither(a), T.chain(f), T.chain(({ tuple: [b, a] }) => pipe( setEither(a), T.chain((a) => value.ref.set(a)), T.as(b) ) ) ) ), S.withPermit(value.semaphore) ) ), DerivedAllM: (derivedAll) => derivedAll.use((value, getEither, setEither) => pipe( value.ref.get, T.chain((s) => pipe( getEither(s), T.chain(f), T.chain(({ tuple: [b, a] }) => pipe( setEither(a)(s), T.chain((a) => value.ref.set(a)), T.as(b) ) ) ) ), S.withPermit(value.semaphore) ) ) }) ) } /** * Atomically modifies the `RefM` with the specified function, which computes * a return value for the modification. This is a more powerful version of * `update`. */ export function modify(f: (a: A) => T.Effect>) { return ( self: XRefM ): T.Effect => modify_(self, f) } /** * Reads the value from the `XRefM`. */ export function get(self: XRefM) { return self.get } /** * Writes a new value to the `RefM`, returning the value immediately before * modification. */ export function getAndSet_(self: XRefM, a: A) { return modify_(self, (v) => T.succeed(Tp.tuple(v, a))) } /** * Writes a new value to the `RefM`, returning the value immediately before * modification. */ export function getAndSet(a: A) { return (self: XRefM) => getAndSet_(self, a) } /** * Atomically modifies the `RefM` with the specified function, returning the * value immediately before modification. */ export function getAndUpdate_( self: XRefM, f: (a: A) => T.Effect ) { return modify_(self, (v) => T.map_(f(v), (r) => Tp.tuple(v, r))) } /** * Atomically modifies the `RefM` with the specified function, returning the * value immediately before modification. */ export function getAndUpdate(f: (a: A) => T.Effect) { return (self: XRefM) => getAndUpdate_(self, f) } /** * Atomically modifies the `RefM` with the specified function, returning the * value immediately before modification. */ export function getAndUpdateSome_( self: XRefM, f: (a: A) => O.Option> ) { return modify_(self, (v) => pipe( f(v), O.getOrElse(() => T.succeed(v)), T.map((r) => Tp.tuple(v, r)) ) ) } /** * Atomically modifies the `RefM` with the specified function, returning the * value immediately before modification. */ export function getAndUpdateSome( f: (a: A) => O.Option> ) { return (self: XRefM) => getAndUpdateSome_(self, f) } /** * 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`. */ export function modifySome_( self: XRefM, def: B, f: (a: A) => O.Option>> ) { return modify_(self, (v) => O.getOrElse_(f(v), () => T.succeed(Tp.tuple(def, v)))) } /** * 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`. */ export function modifySome(def: B) { return (f: (a: A) => O.Option>>) => (self: XRefM) => modifySome_(self, def, f) } /** * Atomically modifies the `RefM` with the specified function. */ export function update_( self: XRefM, f: (a: A) => T.Effect ): T.Effect { return modify_(self, (v) => T.map_(f(v), (r) => Tp.tuple(undefined, r))) } /** * Atomically modifies the `RefM` with the specified function. */ export function update(f: (a: A) => T.Effect) { return ( self: XRefM ): T.Effect => update_(self, f) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateAndGet_( self: XRefM, f: (a: A) => T.Effect ): T.Effect { return modify_(self, (v) => pipe( f(v), T.map((r) => Tp.tuple(r, r)) ) ) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateAndGet(f: (a: A) => T.Effect) { return ( self: XRefM ): T.Effect => updateAndGet_(self, f) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateSome_( self: XRefM, f: (a: A) => O.Option> ): T.Effect { return modify_(self, (v) => pipe( f(v), O.getOrElse(() => T.succeed(v)), T.map((r) => Tp.tuple(undefined, r)) ) ) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateSome(f: (a: A) => O.Option>) { return ( self: XRefM ): T.Effect => updateSome_(self, f) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateSomeAndGet_( self: XRefM, f: (a: A) => O.Option> ): T.Effect { return modify_(self, (v) => pipe( f(v), O.getOrElse(() => T.succeed(v)), T.map((r) => Tp.tuple(r, r)) ) ) } /** * Atomically modifies the `RefM` with the specified function. */ export function updateSomeAndGet( f: (a: A) => O.Option> ) { return ( self: XRefM ): T.Effect => updateSomeAndGet_(self, f) } /** * Folds over the error and value types of the `XRefM`. */ export function fold_( self: XRefM, ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => E.Either, bd: (_: B) => E.Either ): XRefM { return self.foldM( ea, eb, (c) => T.fromEither(() => ca(c)), (b) => T.fromEither(() => bd(b)) ) } /** * Folds over the error and value types of the `XRefM`. */ export function fold( ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => E.Either, bd: (_: B) => E.Either ) { return (self: XRefM): XRefM => self.foldM( ea, eb, (c) => T.fromEither(() => ca(c)), (b) => T.fromEither(() => bd(b)) ) } /** * 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. */ export function foldM_( self: XRefM, ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => T.Effect, bd: (_: B) => T.Effect ): XRefM { return self.foldM(ea, eb, ca, bd) } /** * 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. */ export function foldM( ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => T.Effect, bd: (_: B) => T.Effect ) { return ( self: XRefM ): XRefM => self.foldM(ea, eb, ca, bd) } /** * 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. */ export function foldAllM_( self: XRefM, ea: (_: EA) => EC, eb: (_: EB) => ED, ec: (_: EB) => EC, ca: (_: C) => (_: B) => T.Effect, bd: (_: B) => T.Effect ): XRefM { return self.foldAllM(ea, eb, ec, ca, bd) } /** * 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. */ export function foldAllM( ea: (_: EA) => EC, eb: (_: EB) => ED, ec: (_: EB) => EC, ca: (_: C) => (_: B) => T.Effect, bd: (_: B) => T.Effect ) { return ( self: XRefM ): XRefM => self.foldAllM(ea, eb, ec, ca, bd) } /** * 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`. */ export function collectM_( self: XRefM, f: (b: B) => O.Option> ): XRefM, A, C> { return self.foldM( identity, (_) => O.some(_), (_) => T.succeed(_), (b) => pipe( f(b), O.map((a) => T.asSomeError(a)), O.getOrElse(() => T.fail(O.none)) ) ) } /** * 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`. */ export function collectM(f: (b: B) => O.Option>) { return ( self: XRefM ): XRefM, A, C> => collectM_(self, f) } /** * 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`. */ export function collect_( self: XRefM, f: (b: B) => O.Option ): XRefM, A, C> { return collectM_(self, (b) => pipe(f(b), O.map(T.succeed))) } /** * 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`. */ export function collect(f: (b: B) => O.Option) { return ( self: XRefM ): XRefM, A, C> => collect_(self, f) } /** * Transforms both the `set` and `get` values of the `XRefM` with the * specified effectual functions. */ export function dimapM_( self: XRefM, f: (c: C) => T.Effect, g: (b: B) => T.Effect ) { return self.foldM( (ea: EA | EC) => ea, (eb: EB | ED) => eb, f, g ) } /** * Transforms both the `set` and `get` values of the `XRefM` with the * specified effectual functions. */ export function dimapM( f: (c: C) => T.Effect, g: (b: B) => T.Effect ) { return (self: XRefM) => dimapM_(self, f, g) } /** * Transforms both the `set` and `get` errors of the `XRefM` with the * specified functions. */ export function dimapError_( self: XRefM, f: (ea: EA) => EC, g: (eb: EB) => ED ): XRefM { return fold_( self, (ea) => f(ea), (eb) => g(eb), (a) => E.right(a), (b) => E.right(b) ) } /** * Transforms both the `set` and `get` errors of the `XRefM` with the * specified functions. */ export function dimapError(f: (ea: EA) => EC, g: (eb: EB) => ED) { return ( self: XRefM ): XRefM => dimapError_(self, f, g) } /** * 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`. */ export function filterInputM_( self: XRefM, f: (a: A1) => T.Effect ): XRefM, EB, A1, B> { return foldM_( self, (ea) => O.some(ea), identity, (a: A1) => T.ifM_( T.asSomeError(f(a)), () => T.succeed(a), () => T.fail>(O.none) ), T.succeed ) } /** * 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`. */ export function filterInputM( f: (a: A1) => T.Effect ) { return ( self: XRefM ): XRefM, EB, A1, B> => filterInputM_(self, f) } /** * 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`. */ export function filterInput_( self: XRefM, f: (a: A1) => boolean ): XRefM, EB, A1, B> { return filterInputM_(self, (a) => T.succeed(f(a))) } /** * 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`. */ export function filterInput(f: (a: A1) => boolean) { return ( self: XRefM ): XRefM, EB, A1, B> => filterInput_(self, f) } /** * 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`. */ export function filterOutputM_( self: XRefM, f: (b: B) => T.Effect ): XRefM, A, B> { 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) ) ) } /** * 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`. */ export function filterOutputM(f: (b: B) => T.Effect) { return ( self: XRefM ): XRefM, A, B> => filterOutputM_(self, f) } /** * 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`. */ export function filterOutput_( self: XRefM, f: (b: B) => boolean ): XRefM, A, B> { return filterOutputM_(self, (b) => T.succeed(f(b))) } /** * 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`. */ export function filterOutput(f: (b: B) => boolean) { return ( self: XRefM ): XRefM, A, B> => filterOutput_(self, f) } /** * Transforms the `get` value of the `XRefM` with the specified effectual * function. */ export function mapM_( self: XRefM, f: (b: B) => T.Effect ) { return dimapM_(self, T.succeed, f) } /** * Transforms the `get` value of the `XRefM` with the specified effectual * function. */ export function mapM(f: (b: B) => T.Effect) { return (self: XRefM) => mapM_(self, f) } /** * Transforms the `set` value of the `XRefM` with the specified effectual * function. */ export function contramapM_( self: XRefM, f: (c: C) => T.Effect ): XRefM { return dimapM_(self, f, T.succeed) } /** * Transforms the `set` value of the `XRefM` with the specified effectual * function. */ export function contramapM(f: (c: C) => T.Effect) { return ( self: XRefM ): XRefM => contramapM_(self, f) } /** * Transforms the `set` value of the `XRefM` with the specified function. */ export function contramap_( self: XRefM, f: (c: C) => A ): XRefM { return contramapM_(self, (c) => T.succeed(f(c))) } /** * Transforms the `set` value of the `XRefM` with the specified function. */ export function contramap(f: (c: C) => A) { return ( self: XRefM ): XRefM => contramap_(self, f) } /** * Transforms the `get` value of the `XRefM` with the specified function. */ export function map_( self: XRefM, f: (b: B) => C ) { return mapM_(self, (b) => T.succeed(f(b))) } /** * Transforms the `get` value of the `XRefM` with the specified function. */ export function map(f: (b: B) => C) { return (self: XRefM) => map_(self, f) } /** * Returns a read only view of the `XRefM`. */ export function readOnly( self: XRefM ): XRefM { return self } /** * Returns a read only view of the `XRefM`. */ export function writeOnly( self: XRefM ): XRefM { return fold_( self, identity, (): void => undefined, E.right, () => E.left(undefined) ) } /** * Performs the specified effect every time a value is written to this * `XRefM`. */ export function tapInput_( self: XRefM, f: (a: A1) => T.Effect ) { return contramapM_(self, (c: A1) => pipe(f(c), T.as(c))) } /** * Performs the specified effect every time a value is written to this * `XRefM`. */ export function tapInput( f: (a: A1) => T.Effect ) { return (self: XRefM) => tapInput_(self, f) } /** * Performs the specified effect every time a value is read to this * `XRefM`. */ export function tapOutput_( self: XRefM, f: (b: B) => T.Effect ) { return mapM_(self, (b) => pipe(f(b), T.as(b))) } /** * Performs the specified effect every time a value is read to this * `XRefM`. */ export function tapOutput(f: (b: B) => T.Effect) { return (self: XRefM) => tapOutput_(self, f) }