/** * Stores mutable state whose updates run one at a time. * * A `SynchronizedRef` behaves like a `Ref` for reading and simple state * storage, but update and modify operations are serialized so each change sees * a consistent current value. This is especially useful when the next value is * computed by an effect, because the effectful transition is still protected * from concurrent updates. This module includes constructors, reads, writes, * updates, partial updates, and effectful update helpers. * * @stability stable * @since 2.0.0 */ import * as Effect from "./Effect.ts" import { dual } from "./Function.ts" import { PipeInspectableProto } from "./internal/core.ts" import * as Option from "./Option.ts" import type { Pipeable } from "./Pipeable.ts" import * as Ref from "./Ref.ts" import * as Semaphore from "./Semaphore.ts" const TypeId = "~effect/SynchronizedRef" /** * A mutable reference whose update and modify operations are serialized with an * internal semaphore, including effectful transformations. * * **When to use** * * Use when shared state may be updated by multiple fibers and each update, * including effectful state transitions, must observe one current value and run * one at a time. * * @see {@link Ref.Ref} for a plain `Ref` when updates do not need effectful synchronization * * @stability stable * @category models * @since 2.0.0 */ export interface SynchronizedRef extends Pipeable { readonly [TypeId]: typeof TypeId readonly backing: Ref.Ref readonly semaphore: Semaphore.Semaphore } const Proto = { ...PipeInspectableProto, [TypeId]: TypeId, toJSON(this: SynchronizedRef) { return { _id: "SynchronizedRef", value: this.backing.ref.current } } } /** * Creates a `SynchronizedRef` synchronously from an initial value. * * **When to use** * * Use when you need synchronous `SynchronizedRef` construction outside an * Effect workflow. * * @stability stable * @category constructors * @since 4.0.0 */ export const makeUnsafe = (value: A): SynchronizedRef => { const self = Object.create(Proto) self.semaphore = Semaphore.makeUnsafe(1) self.backing = Ref.makeUnsafe(value) return self } /** * Creates a `SynchronizedRef` from an initial value, wrapped in an `Effect`. * * **When to use** * * Use to create a `SynchronizedRef` inside an Effect program when later updates * may run effects and must be serialized. * * **Details** * * The returned effect constructs a fresh `SynchronizedRef` by delegating to * `makeUnsafe` when the effect is evaluated. * * @see {@link makeUnsafe} for synchronous construction when the caller controls safe initialization * @see {@link Ref.make} for a plain `Ref` when updates do not need effectful synchronization * * @stability stable * @category constructors * @since 2.0.0 */ export const make = (value: A): Effect.Effect> => Effect.sync(() => makeUnsafe(value)) /** * Reads the current value synchronously, bypassing the `Effect` API and the * ref's semaphore. * * **When to use** * * Use when you need immediate synchronous access to a `SynchronizedRef` value * in low-level code that can safely read outside an `Effect`. * * @see {@link get} for the Effect-wrapped read when composing inside Effect programs * * @stability stable * @category getters * @since 4.0.0 */ export const getUnsafe = (self: SynchronizedRef): A => self.backing.ref.current /** * Returns an `Effect` that reads the current value of the `SynchronizedRef`. * * **When to use** * * Use to read the current value of a `SynchronizedRef` inside an `Effect` * program without changing it. * * @see {@link getUnsafe} for synchronous reads when the caller controls safe access outside `Effect` * * @stability stable * @category getters * @since 2.0.0 */ export const get = (self: SynchronizedRef): Effect.Effect => Effect.sync(() => getUnsafe(self)) /** * Sets a new value atomically and returns the previous value, serialized by the * ref's semaphore. * * **When to use** * * Use to replace a `SynchronizedRef` with a known value when the previous value * is also needed. * * @see {@link set} for setting a value without returning the previous value * @see {@link setAndGet} for setting a value and returning the new value * @see {@link getAndUpdate} for deriving the new value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ export const getAndSet: { /** * Sets a new value atomically and returns the previous value, serialized by the * ref's semaphore. * * **When to use** * * Use to replace a `SynchronizedRef` with a known value when the previous value * is also needed. * * @see {@link set} for setting a value without returning the previous value * @see {@link setAndGet} for setting a value and returning the new value * @see {@link getAndUpdate} for deriving the new value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * Sets a new value atomically and returns the previous value, serialized by the * ref's semaphore. * * **When to use** * * Use to replace a `SynchronizedRef` with a known value when the previous value * is also needed. * * @see {@link set} for setting a value without returning the previous value * @see {@link setAndGet} for setting a value and returning the new value * @see {@link getAndUpdate} for deriving the new value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.getAndSet(self.backing, value)) ) /** * Updates the current value atomically with a function and returns the previous * value, serialized by the ref's semaphore. * * **When to use** * * Use to run a pure `SynchronizedRef` state update when the previous stored * value is also needed. * * @see {@link update} for updating without returning a value * @see {@link updateAndGet} for updating and returning the new value * @see {@link getAndUpdateEffect} for effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const getAndUpdate: { /** * Updates the current value atomically with a function and returns the previous * value, serialized by the ref's semaphore. * * **When to use** * * Use to run a pure `SynchronizedRef` state update when the previous stored * value is also needed. * * @see {@link update} for updating without returning a value * @see {@link updateAndGet} for updating and returning the new value * @see {@link getAndUpdateEffect} for effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * Updates the current value atomically with a function and returns the previous * value, serialized by the ref's semaphore. * * **When to use** * * Use to run a pure `SynchronizedRef` state update when the previous stored * value is also needed. * * @see {@link update} for updating without returning a value * @see {@link updateAndGet} for updating and returning the new value * @see {@link getAndUpdateEffect} for effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.getAndUpdate(self.backing, f)) ) /** * Runs an effectful update atomically while holding the ref's semaphore, sets * the new value if the effect succeeds, and returns the previous value. * * **When to use** * * Use when you need an effectful `SynchronizedRef` state transition to return * the previous stored value. * * @see {@link getAndUpdate} for pure updates that return the previous value * @see {@link updateEffect} for effectful updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that return the new value * @see {@link modifyEffect} for effectful updates with a custom return value * @see {@link getAndUpdateSomeEffect} for conditional effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const getAndUpdateEffect: { /** * Runs an effectful update atomically while holding the ref's semaphore, sets * the new value if the effect succeeds, and returns the previous value. * * **When to use** * * Use when you need an effectful `SynchronizedRef` state transition to return * the previous stored value. * * @see {@link getAndUpdate} for pure updates that return the previous value * @see {@link updateEffect} for effectful updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that return the new value * @see {@link modifyEffect} for effectful updates with a custom return value * @see {@link getAndUpdateSomeEffect} for conditional effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful update atomically while holding the ref's semaphore, sets * the new value if the effect succeeds, and returns the previous value. * * **When to use** * * Use when you need an effectful `SynchronizedRef` state transition to return * the previous stored value. * * @see {@link getAndUpdate} for pure updates that return the previous value * @see {@link updateEffect} for effectful updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that return the new value * @see {@link modifyEffect} for effectful updates with a custom return value * @see {@link getAndUpdateSomeEffect} for conditional effectful updates that return the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue return value }) })) ) /** * Applies a partial update atomically and returns the previous value. If the * function returns `Option.some`, the ref is updated; if it returns * `Option.none`, the ref is left unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while applying a pure * conditional update. * * @see {@link getAndUpdate} for always applying a pure update * @see {@link updateSome} for applying a pure conditional update without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const getAndUpdateSome: { /** * Applies a partial update atomically and returns the previous value. If the * function returns `Option.some`, the ref is updated; if it returns * `Option.none`, the ref is left unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while applying a pure * conditional update. * * @see {@link getAndUpdate} for always applying a pure update * @see {@link updateSome} for applying a pure conditional update without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * Applies a partial update atomically and returns the previous value. If the * function returns `Option.some`, the ref is updated; if it returns * `Option.none`, the ref is left unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while applying a pure * conditional update. * * @see {@link getAndUpdate} for always applying a pure update * @see {@link updateSome} for applying a pure conditional update without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.getAndUpdateSome(self.backing, pf)) ) /** * Runs an effectful partial update atomically while holding the ref's semaphore * and returns the previous value. `Option.some` updates the ref; `Option.none` * leaves it unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while running an effectful * conditional update. * * @see {@link getAndUpdateSome} for the pure conditional variant * @see {@link updateSomeEffect} for effectful conditional updates without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const getAndUpdateSomeEffect: { /** * Runs an effectful partial update atomically while holding the ref's semaphore * and returns the previous value. `Option.some` updates the ref; `Option.none` * leaves it unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while running an effectful * conditional update. * * @see {@link getAndUpdateSome} for the pure conditional variant * @see {@link updateSomeEffect} for effectful conditional updates without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful partial update atomically while holding the ref's semaphore * and returns the previous value. `Option.some` updates the ref; `Option.none` * leaves it unchanged. * * **When to use** * * Use to return the previous `SynchronizedRef` value while running an effectful * conditional update. * * @see {@link getAndUpdateSome} for the pure conditional variant * @see {@link updateSomeEffect} for effectful conditional updates without returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), (option) => { if (Option.isNone(option)) { return Effect.succeed(value) } self.backing.ref.current = option.value return Effect.succeed(value) }) })) ) /** * Computes a return value and a new ref value atomically, stores the new value, * and returns the computed result. * * **When to use** * * Use to derive a separate result and the next stored `SynchronizedRef` value * from the same current value in one serialized pure update. * * @see {@link modifyEffect} for effectfully deriving both the result and next stored value * @see {@link modifySome} for deriving a result and optionally updating the stored value * @see {@link updateAndGet} for returning the new stored value instead of a separate result * * @stability stable * @category mutations * @since 2.0.0 */ export const modify: { /** * Computes a return value and a new ref value atomically, stores the new value, * and returns the computed result. * * **When to use** * * Use to derive a separate result and the next stored `SynchronizedRef` value * from the same current value in one serialized pure update. * * @see {@link modifyEffect} for effectfully deriving both the result and next stored value * @see {@link modifySome} for deriving a result and optionally updating the stored value * @see {@link updateAndGet} for returning the new stored value instead of a separate result * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => readonly [B, A]): (self: SynchronizedRef) => Effect.Effect /** * Computes a return value and a new ref value atomically, stores the new value, * and returns the computed result. * * **When to use** * * Use to derive a separate result and the next stored `SynchronizedRef` value * from the same current value in one serialized pure update. * * @see {@link modifyEffect} for effectfully deriving both the result and next stored value * @see {@link modifySome} for deriving a result and optionally updating the stored value * @see {@link updateAndGet} for returning the new stored value instead of a separate result * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => readonly [B, A]): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => readonly [B, A]): Effect.Effect => self.semaphore.withPermit(Ref.modify(self.backing, f)) ) /** * Runs an effectful modification atomically while holding the ref's semaphore, * stores the new value if the effect succeeds, and returns the computed result. * * **When to use** * * Use to effectfully compute both a separate return value and the next stored * `SynchronizedRef` value in one serialized update. * * @see {@link modify} for the pure variant * @see {@link updateEffect} for effectfully storing a new value without a separate result * * @stability stable * @category mutations * @since 2.0.0 */ export const modifyEffect: { /** * Runs an effectful modification atomically while holding the ref's semaphore, * stores the new value if the effect succeeds, and returns the computed result. * * **When to use** * * Use to effectfully compute both a separate return value and the next stored * `SynchronizedRef` value in one serialized update. * * @see {@link modify} for the pure variant * @see {@link updateEffect} for effectfully storing a new value without a separate result * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful modification atomically while holding the ref's semaphore, * stores the new value if the effect succeeds, and returns the computed result. * * **When to use** * * Use to effectfully compute both a separate return value and the next stored * `SynchronizedRef` value in one serialized update. * * @see {@link modify} for the pure variant * @see {@link updateEffect} for effectfully storing a new value without a separate result * * @stability stable * @category mutations * @since 2.0.0 */ ( self: SynchronizedRef, f: (a: A) => Effect.Effect ): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), ([b, a]) => { self.backing.ref.current = a return b }) })) ) /** * Computes a return value and an optional new ref value atomically. * `Option.some` updates the ref; `Option.none` leaves it unchanged. * * **When to use** * * Use to compute a return value while optionally updating a `SynchronizedRef` * under its semaphore. * * @see {@link modify} for always storing a new value * @see {@link updateSome} for optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ export const modifySome: { /** * Computes a return value and an optional new ref value atomically. * `Option.some` updates the ref; `Option.none` leaves it unchanged. * * **When to use** * * Use to compute a return value while optionally updating a `SynchronizedRef` * under its semaphore. * * @see {@link modify} for always storing a new value * @see {@link updateSome} for optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => readonly [B, Option.Option]): (self: SynchronizedRef) => Effect.Effect /** * Computes a return value and an optional new ref value atomically. * `Option.some` updates the ref; `Option.none` leaves it unchanged. * * **When to use** * * Use to compute a return value while optionally updating a `SynchronizedRef` * under its semaphore. * * @see {@link modify} for always storing a new value * @see {@link updateSome} for optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, pf: (a: A) => readonly [B, Option.Option]): Effect.Effect } = dual( 2, ( self: SynchronizedRef, pf: (a: A) => readonly [B, Option.Option] ): Effect.Effect => self.semaphore.withPermit(Ref.modifySome(self.backing, pf)) ) /** * Runs an effectful modification atomically while holding the ref's semaphore. * The effect computes a return value and an optional new ref value; * `Option.some` updates the ref and `Option.none` leaves it unchanged. * * **When to use** * * Use to effectfully compute a return value while optionally updating the * stored `SynchronizedRef` value. * * @see {@link modifySome} for the pure variant * @see {@link updateSomeEffect} for effectful optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ export const modifySomeEffect: { /** * Runs an effectful modification atomically while holding the ref's semaphore. * The effect computes a return value and an optional new ref value; * `Option.some` updates the ref and `Option.none` leaves it unchanged. * * **When to use** * * Use to effectfully compute a return value while optionally updating the * stored `SynchronizedRef` value. * * @see {@link modifySome} for the pure variant * @see {@link updateSomeEffect} for effectful optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Effect.Effect], E, R>): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful modification atomically while holding the ref's semaphore. * The effect computes a return value and an optional new ref value; * `Option.some` updates the ref and `Option.none` leaves it unchanged. * * **When to use** * * Use to effectfully compute a return value while optionally updating the * stored `SynchronizedRef` value. * * @see {@link modifySome} for the pure variant * @see {@link updateSomeEffect} for effectful optional updates without a separate return value * * @stability stable * @category mutations * @since 2.0.0 */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect], E, R> ): Effect.Effect } = dual( 2, ( self: SynchronizedRef, pf: (a: A) => Effect.Effect], E, R> ): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), ([b, maybeA]) => { if (Option.isNone(maybeA)) { return Effect.succeed(b) } self.backing.ref.current = maybeA.value return Effect.succeed(b) }) })) ) /** * Sets the value of the `SynchronizedRef`, serialized by the ref's semaphore. * * **When to use** * * Use to replace the current value of a `SynchronizedRef` with a known value * while keeping the write serialized with other synchronized updates. * * @see {@link getAndSet} for replacing the value when the previous value is needed * @see {@link setAndGet} for replacing the value when the new value should be returned * @see {@link update} for deriving the next value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ export const set: { /** * Sets the value of the `SynchronizedRef`, serialized by the ref's semaphore. * * **When to use** * * Use to replace the current value of a `SynchronizedRef` with a known value * while keeping the write serialized with other synchronized updates. * * @see {@link getAndSet} for replacing the value when the previous value is needed * @see {@link setAndGet} for replacing the value when the new value should be returned * @see {@link update} for deriving the next value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * Sets the value of the `SynchronizedRef`, serialized by the ref's semaphore. * * **When to use** * * Use to replace the current value of a `SynchronizedRef` with a known value * while keeping the write serialized with other synchronized updates. * * @see {@link getAndSet} for replacing the value when the previous value is needed * @see {@link setAndGet} for replacing the value when the new value should be returned * @see {@link update} for deriving the next value from the current value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.set(self.backing, value)) ) /** * Sets the value of the `SynchronizedRef` and returns the new value. * * **When to use** * * Use to replace the current `SynchronizedRef` value with a known value and * return that new value. * * @see {@link set} for setting without returning a value * @see {@link getAndSet} for setting while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const setAndGet: { /** * Sets the value of the `SynchronizedRef` and returns the new value. * * **When to use** * * Use to replace the current `SynchronizedRef` value with a known value and * return that new value. * * @see {@link set} for setting without returning a value * @see {@link getAndSet} for setting while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (value: A): (self: SynchronizedRef) => Effect.Effect /** * Sets the value of the `SynchronizedRef` and returns the new value. * * **When to use** * * Use to replace the current `SynchronizedRef` value with a known value and * return that new value. * * @see {@link set} for setting without returning a value * @see {@link getAndSet} for setting while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, value: A): Effect.Effect } = dual( 2, (self: SynchronizedRef, value: A): Effect.Effect => self.semaphore.withPermit(Ref.setAndGet(self.backing, value)) ) /** * Updates the value of the `SynchronizedRef` with a function, serialized by the * ref's semaphore. * * **When to use** * * Use to apply a pure state transition to a `SynchronizedRef` as a serialized * `Effect`. * * @see {@link updateEffect} for effectfully deriving the next value * @see {@link updateAndGet} for returning the new stored value * @see {@link getAndUpdate} for returning the previous stored value * * @stability stable * @category mutations * @since 2.0.0 */ export const update: { /** * Updates the value of the `SynchronizedRef` with a function, serialized by the * ref's semaphore. * * **When to use** * * Use to apply a pure state transition to a `SynchronizedRef` as a serialized * `Effect`. * * @see {@link updateEffect} for effectfully deriving the next value * @see {@link updateAndGet} for returning the new stored value * @see {@link getAndUpdate} for returning the previous stored value * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * Updates the value of the `SynchronizedRef` with a function, serialized by the * ref's semaphore. * * **When to use** * * Use to apply a pure state transition to a `SynchronizedRef` as a serialized * `Effect`. * * @see {@link updateEffect} for effectfully deriving the next value * @see {@link updateAndGet} for returning the new stored value * @see {@link getAndUpdate} for returning the previous stored value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.update(self.backing, f)) ) /** * Runs an effectful update while holding the ref's semaphore and stores the new * value if the effect succeeds. * * **When to use** * * Use to run an effectful state transition on a `SynchronizedRef` when storing * the new value is the only result you need. * * @see {@link update} for a pure state transition * @see {@link getAndUpdateEffect} for returning the previous stored value * @see {@link updateAndGetEffect} for returning the new stored value * @see {@link modifyEffect} for returning a separate result while storing a new value * @see {@link updateSomeEffect} for effectfully applying only some state transitions * * @stability stable * @category mutations * @since 2.0.0 */ export const updateEffect: { /** * Runs an effectful update while holding the ref's semaphore and stores the new * value if the effect succeeds. * * **When to use** * * Use to run an effectful state transition on a `SynchronizedRef` when storing * the new value is the only result you need. * * @see {@link update} for a pure state transition * @see {@link getAndUpdateEffect} for returning the previous stored value * @see {@link updateAndGetEffect} for returning the new stored value * @see {@link modifyEffect} for returning a separate result while storing a new value * @see {@link updateSomeEffect} for effectfully applying only some state transitions * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful update while holding the ref's semaphore and stores the new * value if the effect succeeds. * * **When to use** * * Use to run an effectful state transition on a `SynchronizedRef` when storing * the new value is the only result you need. * * @see {@link update} for a pure state transition * @see {@link getAndUpdateEffect} for returning the previous stored value * @see {@link updateAndGetEffect} for returning the new stored value * @see {@link modifyEffect} for returning a separate result while storing a new value * @see {@link updateSomeEffect} for effectfully applying only some state transitions * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue }) })) ) /** * Updates the value of the `SynchronizedRef` with a function and returns the * new value. * * **When to use** * * Use to apply a pure `SynchronizedRef` state transition and return the new * stored value. * * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for updating while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ export const updateAndGet: { /** * Updates the value of the `SynchronizedRef` with a function and returns the * new value. * * **When to use** * * Use to apply a pure `SynchronizedRef` state transition and return the new * stored value. * * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for updating while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => A): (self: SynchronizedRef) => Effect.Effect /** * Updates the value of the `SynchronizedRef` with a function and returns the * new value. * * **When to use** * * Use to apply a pure `SynchronizedRef` state transition and return the new * stored value. * * @see {@link update} for updating without returning the new value * @see {@link getAndUpdate} for updating while returning the previous value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => A): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => A): Effect.Effect => self.semaphore.withPermit(Ref.updateAndGet(self.backing, f)) ) /** * Runs an effectful update while holding the ref's semaphore, stores the new * value if the effect succeeds, and returns that new value. * * **When to use** * * Use to run an effectful `SynchronizedRef` state transition and return the new * stored value. * * @see {@link updateEffect} for effectful updates without returning the new value * @see {@link updateAndGet} for the pure variant * * @stability stable * @category mutations * @since 2.0.0 */ export const updateAndGetEffect: { /** * Runs an effectful update while holding the ref's semaphore, stores the new * value if the effect succeeds, and returns that new value. * * **When to use** * * Use to run an effectful `SynchronizedRef` state transition and return the new * stored value. * * @see {@link updateEffect} for effectful updates without returning the new value * @see {@link updateAndGet} for the pure variant * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => Effect.Effect): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful update while holding the ref's semaphore, stores the new * value if the effect succeeds, and returns that new value. * * **When to use** * * Use to run an effectful `SynchronizedRef` state transition and return the new * stored value. * * @see {@link updateEffect} for effectful updates without returning the new value * @see {@link updateAndGet} for the pure variant * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Effect.Effect): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(f(value), (newValue) => { self.backing.ref.current = newValue return newValue }) })) ) /** * Applies a partial update to the current value. `Option.some` stores the new * value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update without returning a * value. * * @see {@link update} for always applying a pure update * @see {@link updateSomeAndGet} for returning the resulting current value * * @stability stable * @category mutations * @since 2.0.0 */ export const updateSome: { /** * Applies a partial update to the current value. `Option.some` stores the new * value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update without returning a * value. * * @see {@link update} for always applying a pure update * @see {@link updateSomeAndGet} for returning the resulting current value * * @stability stable * @category mutations * @since 2.0.0 */ (f: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * Applies a partial update to the current value. `Option.some` stores the new * value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update without returning a * value. * * @see {@link update} for always applying a pure update * @see {@link updateSomeAndGet} for returning the resulting current value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, f: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, f: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.updateSome(self.backing, f)) ) /** * Runs an effectful partial update while holding the ref's semaphore. * `Option.some` stores the new value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update without * returning a value. * * @see {@link updateSome} for the pure conditional variant * @see {@link updateEffect} for effectful updates that always store a new value * * @stability stable * @category mutations * @since 2.0.0 */ export const updateSomeEffect: { /** * Runs an effectful partial update while holding the ref's semaphore. * `Option.some` stores the new value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update without * returning a value. * * @see {@link updateSome} for the pure conditional variant * @see {@link updateEffect} for effectful updates that always store a new value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful partial update while holding the ref's semaphore. * `Option.some` stores the new value; `Option.none` leaves the ref unchanged. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update without * returning a value. * * @see {@link updateSome} for the pure conditional variant * @see {@link updateEffect} for effectful updates that always store a new value * * @stability stable * @category mutations * @since 2.0.0 */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.map(pf(value), (option) => { if (Option.isNone(option)) { return } self.backing.ref.current = option.value }) })) ) /** * Applies a partial update and returns the resulting current value. * `Option.some` stores and returns the new value; `Option.none` returns the * unchanged value. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSome} for conditional updates without returning a value * @see {@link updateAndGet} for always applying a pure update and returning the new value * * @stability stable * @category mutations * @since 2.0.0 */ export const updateSomeAndGet: { /** * Applies a partial update and returns the resulting current value. * `Option.some` stores and returns the new value; `Option.none` returns the * unchanged value. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSome} for conditional updates without returning a value * @see {@link updateAndGet} for always applying a pure update and returning the new value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Option.Option): (self: SynchronizedRef) => Effect.Effect /** * Applies a partial update and returns the resulting current value. * `Option.some` stores and returns the new value; `Option.none` returns the * unchanged value. * * **When to use** * * Use to apply a pure conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSome} for conditional updates without returning a value * @see {@link updateAndGet} for always applying a pure update and returning the new value * * @stability stable * @category mutations * @since 2.0.0 */ (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Option.Option): Effect.Effect => self.semaphore.withPermit(Ref.updateSomeAndGet(self.backing, pf)) ) /** * Runs an effectful partial update while holding the ref's semaphore and * returns the resulting current value. `Option.some` stores and returns the new * value; `Option.none` returns the unchanged value. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSomeEffect} for effectful conditional updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that always store and return a new value * * @stability stable * @category mutations * @since 2.0.0 */ export const updateSomeAndGetEffect: { /** * Runs an effectful partial update while holding the ref's semaphore and * returns the resulting current value. `Option.some` stores and returns the new * value; `Option.none` returns the unchanged value. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSomeEffect} for effectful conditional updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that always store and return a new value * * @stability stable * @category mutations * @since 2.0.0 */ (pf: (a: A) => Effect.Effect, E, R>): (self: SynchronizedRef) => Effect.Effect /** * Runs an effectful partial update while holding the ref's semaphore and * returns the resulting current value. `Option.some` stores and returns the new * value; `Option.none` returns the unchanged value. * * **When to use** * * Use to run an effectful conditional `SynchronizedRef` update and return the * resulting current value. * * @see {@link updateSomeEffect} for effectful conditional updates without returning a value * @see {@link updateAndGetEffect} for effectful updates that always store and return a new value * * @stability stable * @category mutations * @since 2.0.0 */ ( self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R> ): Effect.Effect } = dual( 2, (self: SynchronizedRef, pf: (a: A) => Effect.Effect, E, R>): Effect.Effect => self.semaphore.withPermit(Effect.suspend(() => { const value = getUnsafe(self) return Effect.flatMap(pf(value), (option) => { if (Option.isNone(option)) { return Effect.succeed(value) } self.backing.ref.current = option.value return Effect.succeed(option.value) }) })) )