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