Effect.ts: Absence as First-Class
State Management • Lesson 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