Cache
Caches values loaded by an Effect lookup function.
A cache stores successful and failed lookup results, shares an in-progress lookup when multiple callers request the same missing key, and limits entries by capacity and optional time-to-live rules. This module includes helpers for reading, setting, refreshing, invalidating, and inspecting cache contents.
Combinators
Retrieves all key-value pairs from the cache as an iterable. This function only returns entries with successfully resolved values, filtering out any failed lookups or expired entries.
Gotchas
Expired entries are removed from the cache while entries filters them out.
See
Signature
declare function entries<Key, A, E, R>(self: Cache<Key, A, E, R>): Effect<Iterable<[Key, A], any, any>>Retrieves the value for a key, invoking the lookup function on a cache miss or expired entry.
Details
Concurrent get calls for the same missing key share the same pending
lookup. The cache stores the lookup Exit, so failed lookups are cached and
will fail again until the entry expires, is invalidated, or is refreshed.
Signature
declare const get: { <Key, A>(key: Key): <E, R>(self: Cache<Key, A, E, R>) => Effect<A, E, R>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<A, E, R>;}Example Getting cached values
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Cache miss - triggers lookup function const result1 = yield* Cache.get(cache, "hello")
// Cache hit - returns cached value without lookup const result2 = yield* Cache.get(cache, "hello")
return { result1, result2 }})
const actual = await Effect.runPromise(program)actual // => { result1: 5, result2: 5 }Example Handling lookup failures
import { Cache, Effect, Exit } from "effect"
// Error handling when lookup failsconst program = Effect.gen(function*() { const cache = yield* Cache.make<string, number, string>({ capacity: 10, lookup: (key: string) => key === "error" ? Effect.fail("Lookup failed") : Effect.succeed(key.length) })
// Successful lookup const success = yield* Cache.get(cache, "hello")
// Failed lookup - returns error const failure = yield* Effect.exit(Cache.get(cache, "error")) return [success, failure] as const})
const actual = await Effect.runPromise(program)actual // => [5, Exit.fail("Lookup failed")]Example Sharing concurrent lookups
import { Cache, Effect } from "effect"
// Concurrent access - multiple gets of same key only invoke lookup onceconst program = Effect.gen(function*() { let lookupCount = 0 const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.sync(() => { lookupCount++ return key.length }) })
// Multiple concurrent gets const results = yield* Effect.all([ Cache.get(cache, "hello"), Cache.get(cache, "hello"), Cache.get(cache, "hello") ], { concurrency: "unbounded" })
return { results, lookupCount }})
const actual = await Effect.runPromise(program)actual // => { results: [5, 5, 5], lookupCount: 1 }Reads an existing cache entry without invoking the lookup function.
Details
Returns Option.none() when the key is missing or expired, and Option.some
when a cached lookup has succeeded. If the entry is still pending, waits for
it to complete. If the cached or pending lookup fails, this effect fails with
the same error.
Signature
declare const getOption: { <Key, A>(key: Key): <E, R>(self: Cache<Key, A, E, R>) => Effect<Option<A>, E>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<Option<A>, E>;}Example Reading cached values without lookup
import { Cache, Effect, Option } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// No value in cache yet - returns None without lookup const empty = yield* Cache.getOption(cache, "hello")
// Populate cache using get yield* Cache.get(cache, "hello")
// Now getOption returns the cached value const cached = yield* Cache.getOption(cache, "hello") return [empty, cached] as const})
const actual = await Effect.runPromise(program)actual // => [Option.none(), Option.some(5)]Example Skipping expired entries
import { Cache, Effect, Option } from "effect"import { TestClock } from "effect/testing"
// Expired entries return Noneconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length), timeToLive: "1 hour" })
// Add value to cache yield* Cache.get(cache, "hello")
// Value exists before expiration const beforeExpiry = yield* Cache.getOption(cache, "hello")
// Simulate time passing yield* TestClock.adjust("2 hours")
// Value expired - returns None const afterExpiry = yield* Cache.getOption(cache, "hello") return [beforeExpiry, afterExpiry] as const})
const actual = await Effect.runPromise(Effect.provide(program, TestClock.layer()))actual // => [Option.some(5), Option.none()]Example Waiting for pending lookups
import { Cache, Deferred, Effect, Fiber, Option } from "effect"
// Waits for ongoing computation to completeconst program = Effect.gen(function*() { const deferred = yield* Deferred.make<void>() const cache = yield* Cache.make({ capacity: 10, lookup: (_key: string) => Deferred.await(deferred).pipe(Effect.as(42)) })
// Start lookup in background const getFiber = yield* Effect.forkChild(Cache.get(cache, "key"))
// getOption waits for ongoing computation const optionFiber = yield* Effect.forkChild(Cache.getOption(cache, "key"))
// Complete the computation yield* Deferred.succeed(deferred, void 0)
const result = yield* Fiber.join(optionFiber) const value = yield* Fiber.join(getFiber) return [result, value] as const})
const actual = await Effect.runPromise(program)actual // => [Option.some(42), 42]Retrieves the value associated with the specified key from the cache, only if it contains a resolved successful value.
Details
This checks only an existing non-expired entry. It returns Option.some when
the entry has already resolved successfully, and Option.none for missing,
expired, failed, or still-pending entries.
See
Signature
declare const getSuccess: { <Key, A, R>(key: Key): <E>(self: Cache<Key, A, E, R>) => Effect<Option<A>>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<Option<A>>;}Checks whether the cache contains an entry for the specified key.
Details
This checks for an existing non-expired entry without invoking the cache lookup function. Expired entries are treated as absent.
Signature
declare const has: { <Key, A>(key: Key): <E, R>(self: Cache<Key, A, E, R>) => Effect<boolean>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<boolean>;}Example Checking for cached keys
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length) })
// Check non-existent key const missing = yield* Cache.has(cache, "missing")
// Add entry and check existence yield* Cache.get(cache, "hello") const present = yield* Cache.has(cache, "hello") return [missing, present]})
const actual = await Effect.runPromise(program)actual // => [false, true]Example Checking TTL expiration
import { Cache, Effect } from "effect"import { TestClock } from "effect/testing"
// TTL expiration behaviorconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length), timeToLive: "1 hour" })
// Add entry with TTL yield* Cache.get(cache, "expires") const initial = yield* Cache.has(cache, "expires")
// Still valid before expiration yield* TestClock.adjust("30 minutes") const beforeExpiry = yield* Cache.has(cache, "expires")
// Expired after TTL yield* TestClock.adjust("31 minutes") const afterExpiry = yield* Cache.has(cache, "expires") return [initial, beforeExpiry, afterExpiry]})
const actual = await Effect.runPromise(Effect.provide(program, TestClock.layer()))actual // => [true, true, false]Example Checking multiple keys
import { Cache, Effect } from "effect"
// Checking multiple keys efficientlyconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length) })
// Populate some entries yield* Cache.set(cache, "apple", 5) yield* Cache.set(cache, "banana", 6)
// Check multiple keys const keys = ["apple", "banana", "cherry", "date"] const results: Array<string> = [] for (const key of keys) { const exists = yield* Cache.has(cache, key) results.push(`${key}: ${exists}`) } return results})
const actual = await Effect.runPromise(program)actual // => ["apple: true", "banana: true", "cherry: false", "date: false"]Invalidates the entry associated with the specified key in the cache.
Signature
declare const invalidate: { <Key, A>(key: Key): <E, R>(self: Cache<Key, A, E, R>) => Effect<void>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<void>;}Example Invalidating cached entries
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Add a value to the cache yield* Cache.get(cache, "hello") const beforeInvalidation = yield* Cache.has(cache, "hello")
// Invalidate the entry yield* Cache.invalidate(cache, "hello") const afterInvalidation = yield* Cache.has(cache, "hello")
// Invalidating non-existent keys doesn't error yield* Cache.invalidate(cache, "nonexistent")
// Get after invalidation will invoke lookup again let lookupCount = 0 const cache2 = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.sync(() => { lookupCount++ return key.length }) })
yield* Cache.get(cache2, "test") // lookupCount = 1 yield* Cache.invalidate(cache2, "test") yield* Cache.get(cache2, "test") // lookupCount = 2 (lookup called again) return { beforeInvalidation, afterInvalidation, lookupCount }})
const actual = await Effect.runPromise(program)actual // => { beforeInvalidation: true, afterInvalidation: false, lookupCount: 2 }Invalidates all entries in the cache.
Signature
declare function invalidateAll<Key, A, E, R>(self: Cache<Key, A, E, R>): Effect<void>Example Invalidating all entries
import { Cache, Effect } from "effect"
// Clear all cached entries at onceconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Populate cache with multiple entries yield* Cache.get(cache, "apple") yield* Cache.get(cache, "banana") yield* Cache.get(cache, "cherry")
const sizeBeforeInvalidation = yield* Cache.size(cache) const hasAppleBeforeInvalidation = yield* Cache.has(cache, "apple")
// Clear all entries yield* Cache.invalidateAll(cache)
// Verify cache is empty const sizeAfterInvalidation = yield* Cache.size(cache) const hasAppleAfterInvalidation = yield* Cache.has(cache, "apple") const hasBananaAfterInvalidation = yield* Cache.has(cache, "banana") const hasCherryAfterInvalidation = yield* Cache.has(cache, "cherry") return [ sizeBeforeInvalidation, hasAppleBeforeInvalidation, sizeAfterInvalidation, hasAppleAfterInvalidation, hasBananaAfterInvalidation, hasCherryAfterInvalidation ]})
const actual = await Effect.runPromise(program)actual // => [3, true, 0, false, false, false]Invalidates the entry associated with the specified key in the cache when the predicate returns true for the cached value.
Signature
declare const invalidateWhen: { <Key, A>(key: Key, f: Predicate<A>): <E, R>(self: Cache<Key, A, E, R>) => Effect<boolean>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key, f: Predicate<A>): Effect<boolean>;}Example Invalidating entries conditionally
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Add values to the cache yield* Cache.get(cache, "hello") // value = 5 yield* Cache.get(cache, "hi") // value = 2
// Invalidate when value equals 5 const invalidated1 = yield* Cache.invalidateWhen( cache, "hello", (value) => value === 5 ) const hasHello = yield* Cache.has(cache, "hello")
// Don't invalidate when predicate doesn't match const invalidated2 = yield* Cache.invalidateWhen( cache, "hi", (value) => value === 5 ) const hasHi = yield* Cache.has(cache, "hi")
// Returns false for non-existent keys const invalidated3 = yield* Cache.invalidateWhen( cache, "nonexistent", () => true )
// Returns false for failed cached values const cacheWithErrors = yield* Cache.make<string, number, string>({ capacity: 10, lookup: (key: string) => key === "fail" ? Effect.fail("error") : Effect.succeed(key.length) })
yield* Effect.exit(Cache.get(cacheWithErrors, "fail")) const invalidated4 = yield* Cache.invalidateWhen( cacheWithErrors, "fail", () => true ) return [invalidated1, hasHello, invalidated2, hasHi, invalidated3, invalidated4]})
const actual = await Effect.runPromise(program)actual // => [true, false, false, true, false, false]Retrieves all active keys from the cache, automatically filtering out expired entries.
Signature
declare function keys<Key, A, E, R>(self: Cache<Key, A, E, R>): Effect<Iterable<Key, any, any>>Example Reading active keys
import { Cache, Effect } from "effect"
// Basic key enumerationconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Add some entries to the cache yield* Cache.get(cache, "hello") yield* Cache.get(cache, "world") yield* Cache.get(cache, "cache")
// Retrieve all active keys const keys = yield* Cache.keys(cache) return Array.from(keys).sort()})
const actual = await Effect.runPromise(program)actual // => ["cache", "hello", "world"]Forces a refresh of the value associated with the specified key in the cache.
Details
It will always invoke the lookup function to construct a new value, overwriting any existing value for that key.
Signature
declare const refresh: { <Key, A>(key: Key): <E, R>(self: Cache<Key, A, E, R>) => Effect<A, E, R>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key): Effect<A, E, R>;}Example Refreshing cached values
import { Cache, Effect } from "effect"
// Force refresh of existing cached valuesconst program = Effect.gen(function*() { let counter = 0 const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.sync(() => `${key}-${++counter}`) })
// Initial cache population const value1 = yield* Cache.get(cache, "user")
// Get from cache (no lookup) const value2 = yield* Cache.get(cache, "user")
// Force refresh - always calls lookup const refreshed = yield* Cache.refresh(cache, "user")
// Subsequent gets return refreshed value const value3 = yield* Cache.get(cache, "user") return [value1, value2, refreshed, value3, counter]})
const actual = await Effect.runPromise(program)actual // => ["user-1", "user-1", "user-2", "user-2", 2]Example Resetting TTL on refresh
import { Cache, Effect } from "effect"import { TestClock } from "effect/testing"
// Refresh resets TTL (Time To Live)const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length), timeToLive: "1 hour" })
yield* Cache.get(cache, "test") yield* TestClock.adjust("45 minutes")
// Entry would normally expire in 15 minutes const beforeRefresh = yield* Cache.has(cache, "test")
// Refresh resets the TTL to full 1 hour yield* Cache.refresh(cache, "test") yield* TestClock.adjust("30 minutes")
// Still valid because TTL was reset const afterRefresh = yield* Cache.has(cache, "test") return [beforeRefresh, afterRefresh]})
const actual = await Effect.runPromise(Effect.provide(program, TestClock.layer()))actual // => [true, true]Example Refreshing missing keys
import { Cache, Effect } from "effect"
// Refresh non-existent keysconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(`value-for-${key}`) })
// Refresh non-existent key creates new entry const result = yield* Cache.refresh(cache, "newKey")
// Verify it's now cached const cached = yield* Cache.has(cache, "newKey") return [result, cached]})
const actual = await Effect.runPromise(program)actual // => ["value-for-newKey", true]Sets the value associated with the specified key in the cache. This will overwrite any existing value for that key, skipping the lookup function.
Signature
declare const set: { <Key, A>(key: Key, value: A): <E, R>(self: Cache<Key, A, E, R>) => Effect<void>; <Key, A, E, R>(self: Cache<Key, A, E, R>, key: Key, value: A): Effect<void>;}Example Setting values directly
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length) })
// Set a value directly without invoking lookup yield* Cache.set(cache, "hello", 42) return yield* Cache.get(cache, "hello")})
const actual = await Effect.runPromise(program)actual // => 42Example Overwriting cached values
import { Cache, Effect } from "effect"
// Overwriting existing cached valuesconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length) })
// First get populates via lookup const original = yield* Cache.get(cache, "test") // 4
// Set overwrites the cached value yield* Cache.set(cache, "test", 999) const updated = yield* Cache.get(cache, "test") // 999
return { original, updated }})
const actual = await Effect.runPromise(program)actual // => { original: 4, updated: 999 }Example Applying TTL to set values
import { Cache, Effect } from "effect"import { TestClock } from "effect/testing"
// TTL behavior with set operationsconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length), timeToLive: "1 hour" })
// Set value with TTL applied yield* Cache.set(cache, "temporary", 123) const beforeExpiry = yield* Cache.has(cache, "temporary")
// Advance time past TTL yield* TestClock.adjust("2 hours") const afterExpiry = yield* Cache.has(cache, "temporary") return [beforeExpiry, afterExpiry]})
const actual = await Effect.runPromise(Effect.provide(program, TestClock.layer()))actual // => [true, false]Example Enforcing capacity when setting values
import { Cache, Effect } from "effect"
// Capacity enforcement with set operationsconst program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 2, lookup: (key: string) => Effect.succeed(key.length) })
// Fill cache to capacity yield* Cache.set(cache, "a", 1) yield* Cache.set(cache, "b", 2) const sizeBeforeEviction = yield* Cache.size(cache)
// Adding another entry evicts oldest yield* Cache.set(cache, "c", 3) const sizeAfterEviction = yield* Cache.size(cache) const hasOldest = yield* Cache.has(cache, "a") const hasNewest = yield* Cache.has(cache, "c") return [sizeBeforeEviction, sizeAfterEviction, hasOldest, hasNewest]})
const actual = await Effect.runPromise(program)actual // => [2, 2, false, true]Retrieves the approximate number of entries in the cache.
Details
Note that expired entries are counted until they are accessed and removed. The size reflects the current number of entries stored, not the number of valid entries.
Signature
declare function size<Key, A, E, R>(self: Cache<Key, A, E, R>): Effect<number>Example Reading cache size
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Empty cache has size 0 const emptySize = yield* Cache.size(cache)
// Add entries and check size yield* Cache.get(cache, "hello") yield* Cache.get(cache, "world") const sizeAfterAdding = yield* Cache.size(cache)
// Size decreases after invalidation yield* Cache.invalidate(cache, "hello") const sizeAfterInvalidation = yield* Cache.size(cache) return [emptySize, sizeAfterAdding, sizeAfterInvalidation]})
const actual = await Effect.runPromise(program)actual // => [0, 2, 1]Retrieves all successfully cached values from the cache, excluding failed lookups and expired entries.
Signature
declare function values<Key, A, E, R>(self: Cache<Key, A, E, R>): Effect<Iterable<A, any, any>>Example Reading all cached values
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const cache = yield* Cache.make({ capacity: 10, lookup: (key: string) => Effect.succeed(key.length) })
// Add some values to the cache yield* Cache.get(cache, "a") yield* Cache.get(cache, "ab") yield* Cache.get(cache, "abc")
// Retrieve all cached values const values = yield* Cache.values(cache) return Array.from(values).sort()})
const actual = await Effect.runPromise(program)actual // => [1, 2, 3]Constructors
Creates a cache with a fixed time-to-live for all entries.
Details
This is the basic cache constructor where all entries share the same TTL. The lookup function will be called when a key is not found or has expired.
Signature
declare function make<Key, A, E = never, R = never, ServiceMode extends "lookup" | "construction" = never>(options: { readonly capacity: number; readonly lookup: (key: Key) => Effect<A, E, R>; readonly requireServicesAt?: ServiceMode; readonly timeToLive?: Input;}): Effect<Cache<Key, A, E, "lookup" extends ServiceMode ? R : never>, never, "lookup" extends ServiceMode ? never : R>Example Creating a basic cache
import { Cache, Effect } from "effect"
// Basic cache with string keysconst program = Effect.gen(function*() { const cache = yield* Cache.make<string, number>({ capacity: 100, lookup: (key) => Effect.succeed(key.length) })
const result1 = yield* Cache.get(cache, "hello") const result2 = yield* Cache.get(cache, "world") return { result1, result2 }})
const actual = await Effect.runPromise(program)actual // => { result1: 5, result2: 5 }Example Creating a cache with TTL
import { Cache, Effect } from "effect"
const program = Effect.gen(function*() { const users = new Map([ [123, { name: "Ada", email: "ada@example.com" }], [456, { name: "Grace", email: "grace@example.com" }] ])
const cache = yield* Cache.make< number, { name: string; email: string }, string >({ capacity: 500, lookup: (userId) => Effect.suspend(() => { const user = users.get(userId) return user === undefined ? Effect.fail(`User ${userId} not found`) : Effect.succeed(user) }), timeToLive: "15 minutes" })
const user1 = yield* Cache.get(cache, 123) const user2 = yield* Cache.get(cache, 123) return [user1, user2, user1 === user2] as const})
const actual = await Effect.runPromise(program)actual // => [{ name: "Ada", email: "ada@example.com" }, { name: "Ada", email: "ada@example.com" }, true]Creates a cache with dynamic time-to-live based on the result and key.
When to use
Use when you need different cache entry lifetimes based on the lookup result or key characteristics.
Details
The timeToLive function receives both the exit result and the key, allowing for flexible TTL policies based on success/failure state and key characteristics.
See
- make for a simpler cache constructor with a fixed time-to-live for all entries
Signature
declare function makeWith<Key, A, E = never, R = never, ServiceMode extends "lookup" | "construction" = never>(lookup: (key: Key) => Effect<A, E, R>, options: { readonly capacity: number; readonly requireServicesAt?: ServiceMode; readonly timeToLive?: (exit: Exit<A, E>, key: Key) => Input;}): Effect<Cache<Key, A, E, "lookup" extends ServiceMode ? R : never>, never, "lookup" extends ServiceMode ? never : R>Example Configuring dynamic time to live
import { Cache, Effect, Exit } from "effect"
// Cache with TTL based on computed valueconst program = Effect.gen(function*() { const cache = yield* Cache.makeWith( (id: number) => Effect.succeed({ id, active: id % 2 === 0 }), { capacity: 1000, timeToLive(exit) { if (Exit.isSuccess(exit)) { const user = exit.value return user.active ? "1 hour" : "5 minutes" } return "30 seconds" } } )
return cache.capacity})
const actual = await Effect.runPromise(program)actual // => 1000Models
A cache interface that provides a mutable key-value store with automatic TTL management, capacity limits, and lookup functions for cache misses.
Signature
interface Cache<in out Key, in out A, in out E = never, out R = never> extends Pipeable { readonly "~effect/Cache": "~effect/Cache"; readonly capacity: number; readonly lookup: (key: Key) => Effect<A, E, R>; readonly map: MutableHashMap<Key, Entry<A, E>>; readonly timeToLive: (exit: Exit<A, E>, key: Key) => Duration;}Example Creating a basic cache
import { Cache, Effect } from "effect"
// Basic cache with string keys and number valuesconst program = Effect.gen(function*() { const cache = yield* Cache.make<string, number>({ capacity: 100, lookup: (key: string) => Effect.succeed(key.length) })
// Cache operations const value1 = yield* Cache.get(cache, "hello") // 5 const value2 = yield* Cache.get(cache, "world") // 5 const value3 = yield* Cache.get(cache, "hello") // 5 (cached)
return [value1, value2, value3]})
const actual = await Effect.runPromise(program)actual // => [5, 5, 5]Example Handling lookup failures
import { Cache, Effect, Exit } from "effect"
// Cache with error handlingconst program = Effect.gen(function*() { const cache = yield* Cache.make<string, number, string>({ capacity: 10, lookup: (key: string) => key === "error" ? Effect.fail("Lookup failed") : Effect.succeed(key.length) })
// Handle successful and failed lookups const success = yield* Cache.get(cache, "test") const failure = yield* Effect.exit(Cache.get(cache, "error"))
return [success, failure] as const})
const actual = await Effect.runPromise(program)actual // => [4, Exit.fail("Lookup failed")]Example Using complex keys with TTL
import { Cache, Data, Duration, Effect } from "effect"
// Cache with complex key types and TTLclass UserId extends Data.Class<{ id: number }> {}
const program = Effect.gen(function*() { const userCache = yield* Cache.make<UserId, string>({ capacity: 1000, lookup: (userId: UserId) => Effect.succeed(`User-${userId.id}`), timeToLive: Duration.minutes(5) })
const userId = new UserId({ id: 123 }) const userName = yield* Cache.get(userCache, userId)
return userName})
const actual = await Effect.runPromise(program)actual // => "User-123"Represents a low-level cache entry containing a deferred lookup result and an optional expiration timestamp.
When to use
Use when inspecting a Cache's low-level map and you need the stored
deferred lookup result or expiration timestamp for a key.
Details
An expiresAt value of undefined means the entry does not expire.
Pass the current fiber to await when calling it from inside
Effect.withFiber, so the waiter is counted and its cleanup registered in
that same step. Without it, await resolves the fiber itself.
See
- Cache for the public cache API that manages entries through combinators
Signature
interface Entry<A, E> { awaiters: number; expiresAt: number | undefined; readonly fiber: Fiber<A, E>; await(this: Entry<A, E>, fiber?: Fiber<unknown, unknown>): Effect<A, E>;}