Effect.ts: Absence as First-Class

Advanced PatternsLesson 41 of 51

41. Caching - Memoizing Absence Resolution

Resolve absence once, reuse the result

Code Example
const fetchUser = (id: string) =>
  Effect.gen(function* () {
    yield* Console.log(`Fetching user ${id}...`);
    yield* Effect.sleep("1 second");
    return { id, name: `User ${id}` };
  });

// Cache: only resolve absence once!
const program = Effect.gen(function* () {
  const cached = yield* Effect.cached(
    fetchUser("123"),
    { timeToLive: "5 minutes" }
  );

  // First call: fetches (1 second)
  const user1 = yield* cached;

  // Second call: cached (instant!)
  const user2 = yield* cached;

  // Same user, resolved once
});
Interactive Example
let fetchCount = 0;
const fetchUser = () => Effect.gen(function* () {
      fetchCount++;
      yield* Effect.sleep('50 millis');
      return { name: 'Alice' };
});
const program = Effect.gen(function* () {
      const cached = yield* Effect.cached(fetchUser());
      yield* cached;
      yield* cached;
      yield* cached;
});
await Effect.runPromise(program);
return `Caching!

Called 3 times
Fetched: ${fetchCount} time
Time saved: ~100ms

Memoized absence!`;
Explanation

Caching: resolve expensive absence once, reuse.

Traditional approach: call function repeatedly, waste time. Effect.cached types memoized absence:

  • Data is absent (expensive to fetch)
  • First call: resolve the absence
  • Subsequent calls: reuse resolution
  • No assumptions about data changing
  • TTL: when cached resolution becomes absent again

Absence-first performance: don't re-resolve what's already resolved!


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