# Transactional state (TxRef)

A `TxRef<A>` is the atom of Effect's STM: a mutable reference whose reads and
writes can take part in a transaction. On its own a `TxRef` behaves much like a
[`Ref`](https://effect.plants.sh/state-management/). What makes it special is that, inside an
`Effect.tx` block, every `TxRef` you touch is recorded in a private journal and
committed **together** — so a group of updates becomes a single atomic step that
no other fiber can interleave with.

```ts
import { Effect, TxRef } from "effect"

// A tiny bank: two transactional accounts.
const program = Effect.gen(function*() {
  const checking = yield* TxRef.make(100)
  const savings = yield* TxRef.make(0)

  // Everything inside Effect.tx commits as one atomic unit. Another fiber
  // can never observe a moment where the money has left `checking` but not
  // yet arrived in `savings`.
  yield* Effect.tx(
    Effect.gen(function*() {
      const balance = yield* TxRef.get(checking)
      if (balance < 30) {
        // Aborting the transaction discards every write made above —
        // the journal is thrown away and nothing is committed.
        return yield* Effect.fail("insufficient funds" as const)
      }
      yield* TxRef.set(checking, balance - 30)
      yield* TxRef.update(savings, (amount) => amount + 30)
    })
  )

  return {
    checking: yield* TxRef.get(checking), // 70
    savings: yield* TxRef.get(savings) //   30
  }
})
```

## Creating and reading a reference

`TxRef.make` returns an `Effect` that allocates the reference, so you create one
with `yield*` inside `Effect.gen`. There is also `TxRef.makeUnsafe` for the rare
case where you must construct one outside an Effect (for example, a module-level
singleton).

The four core operations all return `Effect`s:

| Operation | Purpose |
| --- | --- |
| `TxRef.get(ref)` | read the current value |
| `TxRef.set(ref, value)` | replace the value |
| `TxRef.update(ref, f)` | transform the value with `f` |
| `TxRef.modify(ref, f)` | transform the value **and** return a derived result |

`modify` is the most general: its function returns a tuple
`[returnValue, newValue]`, letting you compute a result from the old value while
writing a new one in the same step.

```ts
import { Effect, TxRef } from "effect"

const nextId = (counter: TxRef.TxRef<number>) =>
  // Atomically hand out an id and advance the counter. Returns the id that
  // was just allocated; stores the incremented value.
  TxRef.modify(counter, (current) => [current, current + 1])
```
**Single operations are already atomic:** Each individual `get` / `set` / `update` / `modify` runs in its own implicit
transaction, so a lone update is safe without wrapping it in `Effect.tx`. You
only need an explicit `Effect.tx` boundary when **several** operations must
commit together.

## Composing transactions

The outermost `Effect.tx` call defines the boundary. If you call `Effect.tx`
again *inside* an active transaction, it does not start a new nested
transaction — it joins the current one, reusing the same journal. This means you
can build small transactional helpers and freely compose them; they merge into
whatever transaction is running when they are called.

```ts
import { Effect, TxRef } from "effect"

// Reusable transactional steps. Each is safe to run on its own AND composes
// into a larger transaction when called inside one.
const withdraw = (account: TxRef.TxRef<number>, amount: number) =>
  Effect.tx(TxRef.update(account, (n) => n - amount))

const deposit = (account: TxRef.TxRef<number>, amount: number) =>
  Effect.tx(TxRef.update(account, (n) => n + amount))

const transfer = (
  from: TxRef.TxRef<number>,
  to: TxRef.TxRef<number>,
  amount: number
) =>
  // The outer tx is the real boundary; the inner txs join it, so the two
  // updates commit together as one atomic transfer.
  Effect.tx(
    Effect.gen(function*() {
      yield* withdraw(from, amount)
      yield* deposit(to, amount)
    })
  )
```

## Waiting for a condition with `Effect.txRetry`

Sometimes a transaction can't make progress yet — there isn't enough balance,
the queue is empty, the flag isn't set. Instead of failing or polling, call
`Effect.txRetry`. This **suspends** the transaction and re-runs it only when one
of the `TxRef` values it read changes. It is the building block for every
"block until ready" primitive in the `Tx*` family.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const ref = yield* TxRef.make(0)

  // A producer fiber bumps the value every 100ms.
  yield* Effect.forkChild(
    Effect.forever(
      Effect.tx(TxRef.update(ref, (n) => n + 1)).pipe(Effect.delay("100 millis"))
    )
  )

  // This transaction parks itself until `ref` reaches 10. Each time `ref`
  // changes, Effect wakes the transaction and re-checks the condition —
  // no busy-waiting, no manual sleeping.
  yield* Effect.tx(
    Effect.gen(function*() {
      const value = yield* TxRef.get(ref)
      if (value < 10) {
        return yield* Effect.txRetry
      }
      yield* Effect.log(`reached ${value}`)
    })
  )
})
```

## Optimism and re-execution

Effect's STM is **optimistic**: a transaction runs against its journal and, at
commit time, verifies that nothing it read was changed by another fiber. If a
conflict is detected, the journal is discarded and the body runs again from the
start against fresh values. This is what keeps transactions correct under
contention without any explicit locking.

The practical consequence is that **a transaction body may execute more than
once**. Keep the body pure with respect to the outside world:
**Keep side effects out of the body:** Do not perform logging, HTTP calls, or other observable side effects inside a
transaction body — they could happen several times on retry. Read and write
transactional values inside the transaction, and run side effects *after* it
commits (or make them idempotent).

## TxRef reference

Everything `TxRef` exports is below. The two constructors aside, every operation
returns an `Effect` and is built on top of `modify` — so they all read and write
the transaction journal when a transaction is active, and run in their own
implicit transaction otherwise.

### `TxRef.TxRef<A>`

The transactional reference type. `A` is the stored value type; the parameter is
invariant (`in out A`) because the reference is both read and written. You rarely
construct this directly — use the constructors below — but you will annotate it
when writing helpers over a reference.

```ts
import { Effect, TxRef } from "effect"

// Annotate a parameter so a helper accepts any number-valued reference.
const increment = (ref: TxRef.TxRef<number>) =>
  TxRef.update(ref, (n) => n + 1)

const program = Effect.gen(function*() {
  const counter: TxRef.TxRef<number> = yield* TxRef.make(0)
  yield* increment(counter)
  return yield* TxRef.get(counter)
  // => 1
})
```

### `TxRef.make`

Allocates a new `TxRef` with an initial value, returning an `Effect` that yields
the reference. This is the normal way to create transactional state inside an
`Effect.gen` workflow.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const counter = yield* TxRef.make(0)
  const name = yield* TxRef.make("Alice")
  return [yield* TxRef.get(counter), yield* TxRef.get(name)]
  // => [0, "Alice"]
})
```

### `TxRef.makeUnsafe`

Creates a `TxRef` synchronously, returning the reference directly rather than an
`Effect`. Use it only outside an Effect context — for example a module-level
singleton; prefer `make` inside Effect code.

```ts
import { TxRef } from "effect"

const counter = TxRef.makeUnsafe(0)
const config = TxRef.makeUnsafe({ timeout: 5000, retries: 3 })

console.log(counter.value) // => 0
console.log(config.value) //  => { timeout: 5000, retries: 3 }
```

### `TxRef.get`

Reads the current value of a reference, returning an `Effect<A>`. Reading inside
a transaction records the reference in the journal, so a later write by another
fiber will be detected as a conflict and trigger a retry.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const counter = yield* TxRef.make(42)
  return yield* TxRef.get(counter)
  // => 42
})
```

### `TxRef.set`

Replaces the value of a reference, returning `Effect<void>`. Dual: both
`TxRef.set(ref, value)` and the pipeable `TxRef.set(value)` forms are available.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const counter = yield* TxRef.make(0)
  yield* TxRef.set(counter, 100)
  return yield* TxRef.get(counter)
  // => 100
})
```

### `TxRef.update`

Transforms the value with a function `(current) => next`, returning
`Effect<void>`. Use it when you don't need a result back — just a new value
derived from the old one. Dual, so `ref.pipe(TxRef.update(f))` also works.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const counter = yield* TxRef.make(10)
  yield* TxRef.update(counter, (current) => current * 2)
  return yield* TxRef.get(counter)
  // => 20
})
```

### `TxRef.modify`

The most general operation: the function returns a tuple
`[returnValue, newValue]`. It writes `newValue` into the reference and yields
`returnValue` from the `Effect`. `get`, `set`, and `update` are all defined in
terms of `modify`. Dual, so `ref.pipe(TxRef.modify(f))` also works.

```ts
import { Effect, TxRef } from "effect"

const program = Effect.gen(function*() {
  const counter = yield* TxRef.make(0)

  // Return the doubled current value, store current + 1.
  const result = yield* TxRef.modify(counter, (current) => [current * 2, current + 1])

  return [result, yield* TxRef.get(counter)]
  // => [0, 1]   (returnValue is 0 * 2; new value is 0 + 1)
})
```

## Where to go next

`TxRef` gives you atomic state over plain values. When your state is a
collection — a map, a set, a queue — reach for the purpose-built
[transactional data structures](https://effect.plants.sh/transactions/transactional-data-structures/),
whose every operation already participates in the same journal. For non-STM
fiber coordination (latches, semaphores, queues), see the
[concurrency](https://effect.plants.sh/concurrency/) section.