Effect.ts: Absence as First-Class

State ManagementLesson 47 of 51

28. Ref Intro: Why We Need It

Regular variables in Effect don't work the way you expect - here's why

The Problem
//  This creates subtle bugs!
let counter = 0;

const program = Effect.gen(function* () {
  yield* Effect.sync(() => { counter++; });
  yield* Effect.sync(() => { counter++; });
  return counter;
});

await Effect.runPromise(program); // 2
await Effect.runPromise(program); // 4 
Why This Breaks

Effects are descriptions that can be run multiple times. When you use regular variables:

  • State leaks between runs
  • The same program behaves differently each time
  • It's impossible to reason about
The Solution: Ref
//  This is safe and reusable!
const program = Effect.gen(function* () {
  const counter = yield* Ref.make(0);
  yield* Ref.update(counter, n => n + 1);
  yield* Ref.update(counter, n => n + 1);
  return yield* Ref.get(counter);
});

await Effect.runPromise(program); // 2
await Effect.runPromise(program); // 2 
Key Insight

Ref makes state part of the Effect description.

  • Regular variable: State lives "outside" the Effect (leaks)
  • Ref: State lives "inside" the Effect (contained)

Each time you run a program with Ref.make(), you get fresh state. That's the whole point!


Understanding why Ref exists before learning how to use it


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