Skip to content

MutableRef

A MutableRef<A> is a small synchronous container for mutable state. It holds one current value of type A, exposes it through .current, and offers pipeable helpers for reading, replacing, and transforming the value in place.

Unlike Ref, every MutableRef operation is an ordinary synchronous call — get returns the value directly, set/update return the reference, and getAndSet/updateAndGet return the old or new value. There is no Effect wrapping and nothing to yield*.

import { MutableRef } from "effect"
// `make` returns the reference directly — no Effect, no yield*.
const counter = MutableRef.make(0)
// Read with `get` (or just `.current`).
MutableRef.get(counter) // => 0
// `update` mutates in place and returns the same reference.
MutableRef.update(counter, (n) => n + 1)
MutableRef.get(counter) // => 1
// `set` replaces the value and also returns the reference.
MutableRef.set(counter, 10)
// `.current` is a plain readable/writable field.
counter.current // => 10
counter.current = 20
counter.current // => 20

Reach for MutableRef when state is local, single-fiber, and synchronous — a tight loop, a builder accumulating a result, an Inspectable that tracks a running count, or any place where you would otherwise reach for a plain let but want a stable, pipeable handle you can pass around.

The moment state is shared across fibers, or you need updates to participate in Effect composition, interruption, or fiber coordination, switch to Ref or SynchronizedRef. MutableRef updates are plain imperative mutations: they are not atomic with respect to concurrent fibers and do not return effects.

A typical use is an accumulator inside a synchronous loop:

import { MutableRef } from "effect"
const sumTo = (n: number): number => {
const total = MutableRef.make(0)
for (let i = 1; i <= n; i++) {
MutableRef.update(total, (t) => t + i)
}
return MutableRef.get(total)
}
sumTo(5) // => 15

Two consistent patterns run through the API:

  • The getAnd* helpers (getAndSet, getAndUpdate, getAndIncrement, getAndDecrement) return the previous value.
  • The *AndGet helpers (setAndGet, updateAndGet, incrementAndGet, decrementAndGet) return the new value.

Helpers that mutate without surfacing a value (set, update, increment, decrement, toggle) return the MutableRef itself, so they can be chained or piped.

Most binary operators are dual: you can call them data-first (MutableRef.set(ref, value)) or data-last for piping (MutableRef.set(value)). MutableRef also implements Pipeable and Inspectable, so it can be used with .pipe(...) and prints cleanly via JSON.stringify / Inspectable.

Creates a new MutableRef initialized with the given value. @category constructors

import { MutableRef } from "effect"
const ref = MutableRef.make(42)
MutableRef.get(ref) // => 42
const config = MutableRef.make({ debug: false, timeout: 5000 })
MutableRef.get(config) // => { debug: false, timeout: 5000 }

Reads the current value without mutating the reference. Equivalent to reading .current. @category general

import { MutableRef } from "effect"
const ref = MutableRef.make("hello")
MutableRef.get(ref) // => "hello"
ref.current // => "hello"

Replaces the current value and returns the reference (for chaining). Dual: set(ref, value) or set(value). @category general

import { MutableRef } from "effect"
const ref = MutableRef.make("initial")
const same = MutableRef.set(ref, "updated")
same === ref // => true
MutableRef.get(ref) // => "updated"
// Pipeable (data-last) form
ref.pipe(MutableRef.set("final"))
MutableRef.get(ref) // => "final"

Replaces the current value and returns the new value. Dual. @category general

import { MutableRef } from "effect"
const ref = MutableRef.make("old")
MutableRef.setAndGet(ref, "new") // => "new"
MutableRef.get(ref) // => "new"

Replaces the current value and returns the previous value. Dual. @category general

import { MutableRef } from "effect"
const ref = MutableRef.make("old")
MutableRef.getAndSet(ref, "new") // => "old"
MutableRef.get(ref) // => "new"

Applies f to the current value, stores the result, and returns the reference (for chaining). Dual: update(ref, f) or update(f). @category general

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
const same = MutableRef.update(counter, (n) => n + 1)
same === counter // => true
MutableRef.get(counter) // => 6
// Updates do not clone — return a new value to avoid mutating in place
const user = MutableRef.make({ name: "Alice", age: 30 })
MutableRef.update(user, (u) => ({ ...u, age: u.age + 1 }))
MutableRef.get(user) // => { name: "Alice", age: 31 }

Applies f to the current value, stores the result, and returns the new value. Dual. @category general

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.updateAndGet(counter, (n) => n * 2) // => 10
MutableRef.get(counter) // => 10

Applies f to the current value, stores the result, and returns the previous value. Dual. @category general

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.getAndUpdate(counter, (n) => n + 1) // => 5
MutableRef.get(counter) // => 6

Sets the value to newValue only if the current value equals oldValue, returning true if it swapped and false otherwise. Comparison uses Effect’s Equal semantics, not only JavaScript reference equality. Dual: compareAndSet(ref, oldValue, newValue) or compareAndSet(oldValue, newValue). @category general

import { MutableRef } from "effect"
const ref = MutableRef.make("initial")
// Succeeds: current value matches the expected `oldValue`
MutableRef.compareAndSet(ref, "initial", "updated") // => true
MutableRef.get(ref) // => "updated"
// Fails: current value no longer matches — no change is made
MutableRef.compareAndSet(ref, "initial", "other") // => false
MutableRef.get(ref) // => "updated"

Adds 1 to a numeric reference in place and returns the reference. @category numeric

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.increment(counter)
MutableRef.get(counter) // => 6

Subtracts 1 from a numeric reference in place and returns the reference. @category numeric

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.decrement(counter)
MutableRef.get(counter) // => 4

Adds 1 to a numeric reference and returns the new value (pre-increment, like ++i). @category numeric

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.incrementAndGet(counter) // => 6

Subtracts 1 from a numeric reference and returns the new value. @category numeric

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.decrementAndGet(counter) // => 4

Adds 1 to a numeric reference and returns the previous value (post-increment, like i++). Handy for ID generation. @category numeric

import { MutableRef } from "effect"
const ids = MutableRef.make(0)
MutableRef.getAndIncrement(ids) // => 0
MutableRef.getAndIncrement(ids) // => 1
MutableRef.get(ids) // => 2

Subtracts 1 from a numeric reference and returns the previous value. @category numeric

import { MutableRef } from "effect"
const counter = MutableRef.make(5)
MutableRef.getAndDecrement(counter) // => 5
MutableRef.get(counter) // => 4

Flips a boolean reference between true and false in place and returns the reference. @category boolean

import { MutableRef } from "effect"
const flag = MutableRef.make(false)
MutableRef.toggle(flag)
MutableRef.get(flag) // => true
MutableRef.toggle(flag)
MutableRef.get(flag) // => false