/** * TxRef is a transactional value, it can be read and modified within the body of a transaction. * * Accessed values are tracked by the transaction in order to detect conflicts and in order to * track changes, a transaction will retry whenever a conflict is detected or whenever the * transaction explicitely calls to `Effect.txRetry` and any of the accessed TxRef values * change. * * @since 4.0.0 */ import * as Effect from "./Effect.ts" import { dual } from "./Function.ts" import { pipeArguments } from "./Pipeable.ts" import type { Pipeable } from "./Pipeable.ts" import type { NoInfer } from "./Types.ts" const TypeId = "~effect/transactions/TxRef" /** * TxRef is a transactional value, it can be read and modified within the body of a transaction. * * Accessed values are tracked by the transaction in order to detect conflicts and in order to * track changes, a transaction will retry whenever a conflict is detected or whenever the * transaction explicitely calls to `Effect.txRetry` and any of the accessed TxRef values * change. * * @since 4.0.0 * @category Models * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * // Create a transactional reference * const ref: TxRef.TxRef = yield* TxRef.make(0) * * // Use within a transaction * yield* Effect.tx(Effect.gen(function*() { * const current = yield* TxRef.get(ref) * yield* TxRef.set(ref, current + 1) * })) * * const final = yield* TxRef.get(ref) * console.log(final) // 1 * }) * ``` */ export interface TxRef extends Pipeable { readonly [TypeId]: typeof TypeId version: number pending: Map void> value: A } /** * Creates a new `TxRef` with the specified initial value. * * @since 4.0.0 * @category Constructors * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * // Create a transactional reference with initial value * const counter = yield* TxRef.make(0) * const name = yield* TxRef.make("Alice") * * // Use in transactions * yield* Effect.tx(Effect.gen(function*() { * yield* TxRef.set(counter, 42) * yield* TxRef.set(name, "Bob") * })) * * console.log(yield* TxRef.get(counter)) // 42 * console.log(yield* TxRef.get(name)) // "Bob" * }) * ``` */ export const make = (initial: A) => Effect.sync(() => makeUnsafe(initial)) /** * Creates a new `TxRef` with the specified initial value. * * @since 4.0.0 * @category Constructors * @example * ```ts * import { TxRef } from "effect" * * // Create a TxRef synchronously (unsafe - use make instead in Effect contexts) * const counter = TxRef.makeUnsafe(0) * const config = TxRef.makeUnsafe({ timeout: 5000, retries: 3 }) * * // These are now ready to use in transactions * console.log(counter.value) // 0 * console.log(config.value) // { timeout: 5000, retries: 3 } * ``` */ export const makeUnsafe = (initial: A): TxRef => ({ [TypeId]: TypeId, pending: new Map(), pipe() { return pipeArguments(this, arguments) }, version: 0, value: initial }) /** * Modifies the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Modify and return both old and new value * const result = yield* TxRef.modify(counter, (current) => [current * 2, current + 1]) * * console.log(result) // 0 (the return value: current * 2) * console.log(yield* TxRef.get(counter)) // 1 (the new value: current + 1) * }) * ``` */ export const modify: { /** * Modifies the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Modify and return both old and new value * const result = yield* TxRef.modify(counter, (current) => [current * 2, current + 1]) * * console.log(result) // 0 (the return value: current * 2) * console.log(yield* TxRef.get(counter)) // 1 (the new value: current + 1) * }) * ``` */ (f: (current: NoInfer) => [returnValue: R, newValue: A]): (self: TxRef) => Effect.Effect /** * Modifies the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Modify and return both old and new value * const result = yield* TxRef.modify(counter, (current) => [current * 2, current + 1]) * * console.log(result) // 0 (the return value: current * 2) * console.log(yield* TxRef.get(counter)) // 1 (the new value: current + 1) * }) * ``` */ (self: TxRef, f: (current: A) => [returnValue: R, newValue: A]): Effect.Effect } = dual(2, ( self: TxRef, f: (current: A) => [returnValue: R, newValue: A] ): Effect.Effect => Effect.Transaction.pipe( Effect.flatMap((state) => Effect.sync(() => { if (!state.journal.has(self)) { state.journal.set(self, { version: self.version, value: self.value }) } const current = state.journal.get(self)! const [returnValue, next] = f(current.value) current.value = next return returnValue }) ), Effect.tx )) /** * Updates the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(10) * * // Update the value using a function * yield* Effect.tx( * TxRef.update(counter, (current) => current * 2) * ) * * console.log(yield* TxRef.get(counter)) // 20 * }) * ``` */ export const update: { /** * Updates the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(10) * * // Update the value using a function * yield* Effect.tx( * TxRef.update(counter, (current) => current * 2) * ) * * console.log(yield* TxRef.get(counter)) // 20 * }) * ``` */ (f: (current: NoInfer) => A): (self: TxRef) => Effect.Effect /** * Updates the value of the `TxRef` using the provided function. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(10) * * // Update the value using a function * yield* Effect.tx( * TxRef.update(counter, (current) => current * 2) * ) * * console.log(yield* TxRef.get(counter)) // 20 * }) * ``` */ (self: TxRef, f: (current: A) => A): Effect.Effect } = dual(2, ( self: TxRef, f: (current: A) => A ): Effect.Effect => modify(self, (current) => [void 0, f(current)])) /** * Reads the current value of the `TxRef`. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(42) * * // Read the value within a transaction * const value = yield* Effect.tx( * TxRef.get(counter) * ) * * console.log(value) // 42 * }) * ``` */ export const get = (self: TxRef): Effect.Effect => modify(self, (current) => [current, current]) /** * Sets the value of the `TxRef`. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Set a new value within a transaction * yield* Effect.tx( * TxRef.set(counter, 100) * ) * * console.log(yield* TxRef.get(counter)) // 100 * }) * ``` */ export const set: { /** * Sets the value of the `TxRef`. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Set a new value within a transaction * yield* Effect.tx( * TxRef.set(counter, 100) * ) * * console.log(yield* TxRef.get(counter)) // 100 * }) * ``` */ (value: A): (self: TxRef) => Effect.Effect /** * Sets the value of the `TxRef`. * * @since 4.0.0 * @category Combinators * @example * ```ts * import { Effect, TxRef } from "effect" * * const program = Effect.gen(function*() { * const counter = yield* TxRef.make(0) * * // Set a new value within a transaction * yield* Effect.tx( * TxRef.set(counter, 100) * ) * * console.log(yield* TxRef.get(counter)) // 100 * }) * ``` */ (self: TxRef, value: A): Effect.Effect } = dual(2, ( self: TxRef, value: A ): Effect.Effect => update(self, () => value))