Effect.ts: Absence as First-Class

State ManagementLesson 29 of 51

29. Ref - Mutable State as Controlled Absence

Ref models state as values that are temporarily absent during updates

Code Example
const program = Effect.gen(function* () {
  // Create: counter is "absent" (needs initialization)
  const counter = yield* Ref.make(0);

  // Read: resolve current absence
  const value1 = yield* Ref.get(counter);  // 0

  // Update: describe HOW to resolve next absence
  yield* Ref.update(counter, n => n + 1);

  const value2 = yield* Ref.get(counter);  // 1

  // Atomic updates - no race conditions!
  yield* Ref.updateAndGet(counter, n => n * 2);  // 2

  return yield* Ref.get(counter);
});

// State changes are EFFECTS
// No direct mutation - typed absence resolution!
Interactive Example
const { Ref } = await import('effect');
const program = Effect.gen(function* () {
      const counter = yield* Ref.make(0);
      const v1 = yield* Ref.get(counter);
      yield* Ref.update(counter, n => n + 1);
      const v2 = yield* Ref.get(counter);
      yield* Ref.updateAndGet(counter, n => n * 2);
      const v3 = yield* Ref.get(counter);
      return [v1, v2, v3];
});
const [v1, v2, v3] = await Effect.runPromise(program);
return `Ref state!

Initial: ${v1}${"
After}${1: "}${v2}
After *2: ${v3}

Safe mutable state!`;
Explanation

Ref: state as temporarily absent values.

Traditional approach: mutate variables directly, race conditions everywhere. Ref types state as controlled absence:

  • State value is "absent" during updates
  • No assumptions about read/write order
  • Ref.get: describe reading currently-absent state
  • Ref.update: describe transforming absent state
  • Atomic: state is never "partially absent"

Absence-first mutation: state changes are typed absence transformations!


Part 29 of 51 in the Effect.ts Absence Modeling series