// ets_tracing: off import * as Tp from "../Collections/Immutable/Tuple/index.js" import * as absolve from "../Effect/absolve.js" import * as E from "../Either/index.js" import { identity, pipe } from "../Function/index.js" import * as O from "../Option/index.js" import { AtomicReference } from "../Support/AtomicReference/index.js" import { matchTag } from "../Utils/index.js" import * as A from "./atomic.js" import * as T from "./effect.js" import type { Ref, XRef } from "./XRef.js" import { Atomic, concrete } from "./XRef.js" /** * Creates a new `XRef` with the specified value. */ export function makeRef(a: A): T.UIO> { return T.succeedWith(() => new Atomic(new AtomicReference(a))) } /** * Creates a new `XRef` with the specified value. */ export function unsafeMakeRef(a: A): Ref { return new Atomic(new AtomicReference(a)) } /** * Maps and filters the `get` value of the `XRef` with the specified partial * function, returning a `XRef` with a `get` value that succeeds with the * result of the partial function if it is defined or else fails with `None`. * * @ets_data_first collect_ */ export function collect(pf: (_: B) => O.Option) { return (self: XRef): XRef, A, C> => collect_(self, pf) } /** * Maps and filters the `get` value of the `XRef` with the specified partial * function, returning a `XRef` 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: XRef, pf: (_: B) => O.Option ): XRef, A, C> { return self.fold(identity, O.some, E.right, (b) => E.fromOption_(pf(b), () => O.none)) } /** * Transforms the `set` value of the `XRef` with the specified function. * * @ets_data_first contramap_ */ export function contramap(f: (_: C) => A) { return (self: XRef): XRef => contramap_(self, f) } /** * Transforms the `set` value of the `XRef` with the specified function. */ export function contramap_( self: XRef, f: (_: C) => A ): XRef { return contramapEither_(self, (c) => E.right(f(c))) } /** * Transforms the `set` value of the `XRef` with the specified fallible * function. * * @ets_data_first contramapEither_ */ export function contramapEither(f: (_: C) => E.Either) { return (self: XRef): XRef => contramapEither_(self, f) } /** * Transforms the `set` value of the `XRef` with the specified fallible * function. */ export function contramapEither_( self: XRef, f: (_: C) => E.Either ): XRef { return dimapEither_(self, f, (x) => E.right(x)) } /** * Transforms both the `set` and `get` values of the `XRef` with the * specified functions. * * @ets_data_first dimap_ */ export function dimap(f: (_: C) => A, g: (_: B) => D) { return (self: XRef): XRef => dimap_(self, f, g) } /** * Transforms both the `set` and `get` values of the `XRef` with the * specified functions. */ export function dimap_( self: XRef, f: (_: C) => A, g: (_: B) => D ): XRef { return dimapEither_( self, (c) => E.right(f(c)), (b) => E.right(g(b)) ) } /** * Transforms both the `set` and `get` values of the `XRef` with the * specified fallible functions. * * @ets_data_first dimapEither_ */ export function dimapEither( f: (_: C) => E.Either, g: (_: B) => E.Either ) { return (self: XRef): XRef => dimapEither_(self, f, g) } /** * Transforms both the `set` and `get` values of the `XRef` with the * specified fallible functions. */ export function dimapEither_( self: XRef, f: (_: C) => E.Either, g: (_: B) => E.Either ): XRef { return self.fold( (ea: EA | EC) => ea, (eb: EB | ED) => eb, f, g ) } /** * Transforms both the `set` and `get` errors of the `XRef` with the * specified functions. * * @ets_data_first dimapError_ */ export function dimapError( f: (_: EA) => EC, g: (_: EB) => ED ): (self: XRef) => XRef { return (self) => dimapError_(self, f, g) } /** * Transforms both the `set` and `get` errors of the `XRef` with the * specified functions. */ export function dimapError_( self: XRef, f: (_: EA) => EC, g: (_: EB) => ED ): XRef { return self.fold(f, g, E.right, E.right) } /** * Filters the `set` value of the `XRef` with the specified predicate, * returning a `XRef` with a `set` value that succeeds if the predicate is * satisfied or else fails with `None`. * * @ets_data_first filterInput_ */ export function filterInput(f: (_: A1) => boolean) { return (self: XRef): XRef, EB, A1, B> => filterInput_(self, f) } /** * Filters the `set` value of the `XRef` with the specified predicate, * returning a `XRef` with a `set` value that succeeds if the predicate is * satisfied or else fails with `None`. */ export function filterInput_( self: XRef, f: (_: A1) => boolean ): XRef, EB, A1, B> { return self.fold( O.some, identity, (a) => (f(a) ? E.right(a) : E.left(O.none)), E.right ) } /** * Filters the `get` value of the `XRef` with the specified predicate, * returning a `XRef` with a `get` value that succeeds if the predicate is * satisfied or else fails with `None`. * * @ets_data_first filterOutput_ */ export function filterOutput(f: (_: B) => boolean) { return (_: XRef): XRef, A, B> => filterOutput_(_, f) } /** * Filters the `get` value of the `XRef` with the specified predicate, * returning a `XRef` with a `get` value that succeeds if the predicate is * satisfied or else fails with `None`. */ export function filterOutput_( _: XRef, f: (_: B) => boolean ): XRef, A, B> { return _.fold(identity, O.some, E.right, (b) => (f(b) ? E.right(b) : E.left(O.none))) } /** * Transforms the `get` value of the `XRef` with the specified function. * * @ets_data_first map_ */ export function map(f: (_: B) => C) { return (_: XRef): XRef => map_(_, f) } /** * Transforms the `get` value of the `XRef` with the specified function. */ export function map_( _: XRef, f: (_: B) => C ): XRef { return mapEither_(_, (b) => E.right(f(b))) } /** * Transforms the `get` value of the `XRef` with the specified fallible * function. * * @ets_data_first mapEither_ */ export function mapEither(f: (_: B) => E.Either) { return (_: XRef): XRef => mapEither_(_, f) } /** * Transforms the `get` value of the `XRef` with the specified fallible * function. */ export function mapEither_( _: XRef, f: (_: B) => E.Either ): XRef { return dimapEither_(_, (a) => E.right(a), f) } /** * Returns a read only view of the `XRef`. * * @ets_optimize identity */ export function readOnly(_: XRef): XRef { return _ } /** * Returns a write only view of the `XRef`. */ export function writeOnly( _: XRef ): XRef { return _.fold( identity, () => undefined, E.right, () => E.left(undefined) ) } /** * Atomically modifies the `XRef` with the specified function, which * computes a return value for the modification. This is a more powerful * version of `update`. * * @ets_data_first modify_ */ export function modify(f: (a: A) => Tp.Tuple<[B, A]>) { return (self: XRef): T.IO => modify_(self, f) } /** * Atomically modifies the `XRef` with the specified function, which * computes a return value for the modification. This is a more powerful * version of `update`. */ export function modify_( self: XRef, f: (a: A) => Tp.Tuple<[B, A]> ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.modify(_, f), Derived: (self) => self.use((value, getEither, setEither) => pipe( A.modify(value, (s) => pipe( s, getEither, E.fold( (e) => Tp.tuple(E.left(e), s), (a1) => pipe(f(a1), ({ tuple: [b, a2] }) => pipe( a2, setEither, E.fold( (e) => Tp.tuple(E.left(e), s), (s) => Tp.tuple(E.widenE()(E.right(b)), s) ) ) ) ) ) ), absolve.absolve ) ), DerivedAll: (self) => self.use((value, getEither, setEither) => pipe( A.modify(value, (s) => pipe( s, getEither, E.fold( (e) => Tp.tuple(E.left(e), s), (a1) => pipe(f(a1), ({ tuple: [b, a2] }) => pipe( setEither(a2)(s), E.fold( (e) => Tp.tuple(E.left(e), s), (s) => Tp.tuple(E.widenE()(E.right(b)), s) ) ) ) ) ) ), absolve.absolve ) ) }) ) } /** * Atomically modifies the `XRef` with the specified partial function, * which computes a return value for the modification if the function is * defined on the current value otherwise it returns a default value. This * is a more powerful version of `updateSome`. * * @ets_data_first modifySome_ */ export function modifySome(def: B, f: (a: A) => O.Option>) { return (self: XRef): T.IO => modifySome_(self, def, f) } /** * Atomically modifies the `XRef` with the specified partial function, * which computes a return value for the modification if the function is * defined on the current value otherwise it returns a default value. This * is a more powerful version of `updateSome`. */ export function modifySome_( self: XRef, def: B, f: (a: A) => O.Option> ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.modifySome(_, def, f) }, (_) => modify_(_, (a) => O.getOrElse_(f(a), () => Tp.tuple(def, a))) ) ) } /** * Atomically writes the specified value to the `XRef`, returning the value * immediately before modification. * * @ets_data_first getAndSet_ */ export function getAndSet(a: A) { return (self: XRef): T.IO => getAndSet_(self, a) } /** * Atomically writes the specified value to the `XRef`, returning the value * immediately before modification. */ export function getAndSet_( self: XRef, a: A ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.getAndSet(_, a) }, (_) => modify_(_, (v) => Tp.tuple(v, a)) ) ) } /** * Atomically modifies the `XRef` with the specified function, returning * the value immediately before modification. * * @ets_data_first getAndUpdate_ */ export function getAndUpdate(f: (a: A) => A) { return (self: XRef) => getAndUpdate_(self, f) } /** * Atomically modifies the `XRef` with the specified function, returning * the value immediately before modification. */ export function getAndUpdate_( self: XRef, f: (a: A) => A ): T.IO { return pipe( self, concrete, matchTag( { Atomic: (_) => A.getAndUpdate(_, f) }, modify((v) => Tp.tuple(v, f(v))) ) ) } /** * Atomically modifies the `XRef` with the specified partial function, * returning the value immediately before modification. If the function is * undefined on the current value it doesn't change it. * * @ets_data_first getAndUpdateSome_ */ export function getAndUpdateSome(f: (a: A) => O.Option) { return (self: XRef) => getAndUpdateSome_(self, f) } /** * Atomically modifies the `XRef` with the specified partial function, * returning the value immediately before modification. If the function is * undefined on the current value it doesn't change it. */ export function getAndUpdateSome_( self: XRef, f: (a: A) => O.Option ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.getAndUpdateSome(_, f) }, (_) => modify_(_, (v) => pipe( f(v), O.getOrElse(() => v), (a) => Tp.tuple(v, a) ) ) ) ) } /** * Atomically modifies the `XRef` with the specified function. * * @ets_data_first update_ */ export function update(f: (a: A) => A) { return (self: XRef): T.IO => update_(self, f) } /** * Atomically modifies the `XRef` with the specified function. */ export function update_( self: XRef, f: (a: A) => A ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.update(_, f) }, (_) => modify_(_, (v) => Tp.tuple(undefined, f(v))) ) ) } /** * Atomically modifies the `XRef` with the specified function and returns * the updated value. * * @ets_data_first updateAndGet_ */ export function updateAndGet(f: (a: A) => A) { return (self: XRef): T.IO => updateAndGet_(self, f) } /** * Atomically modifies the `XRef` with the specified function and returns * the updated value. */ export function updateAndGet_( self: XRef, f: (a: A) => A ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.updateAndGet(_, f) }, (self) => pipe( modify_(self, (v) => pipe(f(v), (result) => Tp.tuple(result, result))), T.chain(() => self.get) ) ) ) } /** * Atomically modifies the `XRef` with the specified partial function. If * the function is undefined on the current value it doesn't change it. * * @ets_data_first updateSome_ */ export function updateSome(f: (a: A) => O.Option) { return (self: XRef): T.IO => updateSome_(self, f) } /** * Atomically modifies the `XRef` with the specified partial function. If * the function is undefined on the current value it doesn't change it. */ export function updateSome_( self: XRef, f: (a: A) => O.Option ): T.IO { return pipe( self, concrete, matchTag( { Atomic: (_) => A.updateSome(_, f) }, modify((v) => pipe( f(v), O.getOrElse(() => v), (a) => Tp.tuple(undefined, a) ) ) ) ) } /** * Atomically modifies the `XRef` with the specified partial function. If * the function is undefined on the current value it returns the old value * without changing it. * * @ets_data_first updateSomeAndGet_ */ export function updateSomeAndGet(f: (a: A) => O.Option) { return (self: XRef): T.IO => updateSomeAndGet_(self, f) } /** * Atomically modifies the `XRef` with the specified partial function. If * the function is undefined on the current value it returns the old value * without changing it. */ export function updateSomeAndGet_( self: XRef, f: (a: A) => O.Option ): T.IO { return pipe( self, concrete, matchTag({ Atomic: (_) => A.updateSomeAndGet(_, f) }, (_) => modify_(_, (v) => pipe( f(v), O.getOrElse(() => v), (result) => Tp.tuple(result, result) ) ) ) ) } /** * Folds over the error and value types of the `XRef`. This is a highly * polymorphic method that is capable of arbitrarily transforming the error * and value types of the `XRef`. For most use cases one of the more specific * combinators implemented in terms of `fold` will be more ergonomic but this * method is extremely useful for implementing new combinators. * * @ets_data_first fold_ */ export function fold( ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => E.Either, bd: (_: B) => E.Either ) { return (self: XRef): XRef => self.fold(ea, eb, ca, bd) } /** * Folds over the error and value types of the `XRef`. This is a highly * polymorphic method that is capable of arbitrarily transforming the error * and value types of the `XRef`. For most use cases one of the more specific * combinators implemented in terms of `fold` will be more ergonomic but this * method is extremely useful for implementing new combinators. */ export function fold_( self: XRef, ea: (_: EA) => EC, eb: (_: EB) => ED, ca: (_: C) => E.Either, bd: (_: B) => E.Either ): XRef { return self.fold(ea, eb, ca, bd) } /** * Folds over the error and value types of the `XRef`, allowing access to * the state in transforming the `set` value. This is a more powerful version * of `fold` but requires unifying the error types. * * @ets_data_first foldAll_ */ export function foldAll( ea: (_: EA) => EC, eb: (_: EB) => ED, ec: (_: EB) => EC, ca: (_: C) => (_: B) => E.Either, bd: (_: B) => E.Either ) { return (self: XRef): XRef => self.foldAll(ea, eb, ec, ca, bd) } /** * Folds over the error and value types of the `XRef`, allowing access to * the state in transforming the `set` value. This is a more powerful version * of `fold` but requires unifying the error types. */ export function foldAll_( self: XRef, ea: (_: EA) => EC, eb: (_: EB) => ED, ec: (_: EB) => EC, ca: (_: C) => (_: B) => E.Either, bd: (_: B) => E.Either ): XRef { return self.foldAll(ea, eb, ec, ca, bd) } /** * Reads the value from the `XRef`. */ export function get(self: XRef) { return self.get } /** * Writes a new value to the `XRef`, with a guarantee of immediate * consistency (at some cost to performance). * * @ets_data_first set_ */ export function set(a: A) { return (self: XRef) => self.set(a) } /** * Writes a new value to the `XRef`, with a guarantee of immediate * consistency (at some cost to performance). */ export function set_(self: XRef, a: A) { return self.set(a) }