Skip to main content
A Ref is a mutable reference to a value that can be safely shared and modified across fibers. All operations on a Ref are effectful and atomic.

Type Signature

interface Ref<in out A> extends Effect<A> {
  modify<B>(f: (a: A) => readonly [B, A]): Effect<B>
}

Creating Refs

make

Creates a new Ref with an initial value.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(0)
  console.log(ref)
})

Reading Values

get

Retrieves the current value of a Ref.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(42)
  const value = yield* Ref.get(ref)
  console.log(value)  // 42
})

Updating Values

import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(0)
  yield* Ref.set(ref, 42)
  const value = yield* Ref.get(ref)
  console.log(value)  // 42
})

Atomic Operations

getAndSet

Atomically retrieves the current value and sets a new one.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  const old = yield* Ref.getAndSet(ref, 20)
  
  console.log(old)  // 10
  
  const current = yield* Ref.get(ref)
  console.log(current)  // 20
})

getAndUpdate

Atomically retrieves the current value and updates it.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  const old = yield* Ref.getAndUpdate(ref, n => n * 2)
  
  console.log(old)  // 10
  
  const current = yield* Ref.get(ref)
  console.log(current)  // 20
})

updateAndGet

Updates the value and returns the new value.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  const updated = yield* Ref.updateAndGet(ref, n => n * 2)
  
  console.log(updated)  // 20
})

setAndGet

Sets a new value and returns it.
import { Effect, Ref } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  const value = yield* Ref.setAndGet(ref, 42)
  
  console.log(value)  // 42
})

Conditional Updates

updateSome

Updates the value only if a condition is met.
import { Effect, Ref, Option } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  
  yield* Ref.updateSome(ref, n => 
    n > 5 ? Option.some(n * 2) : Option.none()
  )
  
  const value = yield* Ref.get(ref)
  console.log(value)  // 20
})

modifySome

Modifies the value conditionally and returns a result.
import { Effect, Ref, Option } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(10)
  
  const result = yield* Ref.modifySome(
    ref,
    "unchanged",
    n => n > 5 ? Option.some([n, n * 2]) : Option.none()
  )
  
  console.log(result)  // 10
})

Counter Example

import { Effect, Ref } from "effect"

const makeCounter = Effect.gen(function* () {
  const ref = yield* Ref.make(0)
  
  return {
    increment: Ref.update(ref, n => n + 1),
    decrement: Ref.update(ref, n => n - 1),
    get: Ref.get(ref)
  }
})

const program = Effect.gen(function* () {
  const counter = yield* makeCounter
  
  yield* counter.increment
  yield* counter.increment
  yield* counter.decrement
  
  const value = yield* counter.get
  console.log(value)  // 1
})

Shared State Example

import { Effect, Ref, Fiber } from "effect"

const program = Effect.gen(function* () {
  const ref = yield* Ref.make(0)
  
  const fiber1 = yield* Effect.fork(
    Effect.forEach(
      Array.from({ length: 100 }, (_, i) => i),
      () => Ref.update(ref, n => n + 1),
      { concurrency: "unbounded" }
    )
  )
  
  const fiber2 = yield* Effect.fork(
    Effect.forEach(
      Array.from({ length: 100 }, (_, i) => i),
      () => Ref.update(ref, n => n + 1),
      { concurrency: "unbounded" }
    )
  )
  
  yield* Fiber.join(fiber1)
  yield* Fiber.join(fiber2)
  
  const value = yield* Ref.get(ref)
  console.log(value)  // 200 (always consistent)
})
All Ref operations are atomic, ensuring thread-safe updates even with concurrent access from multiple fibers.

Use Cases

  • Shared mutable state across fibers
  • Counters and accumulators
  • Caching
  • State machines
  • Resource pools

Key Operations

OperationDescription
makeCreates a new Ref
getReads the current value
setSets a new value
updateUpdates the value with a function
modifyUpdates and returns a result
getAndSetGets old value, sets new one
getAndUpdateGets old value, updates
updateAndGetUpdates, returns new value
updateSomeConditional update
modifySomeConditional modify

Build docs developers (and LLMs) love