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]getSuccess
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"]invalidate
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 }invalidateAll
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]invalidateWhen
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.
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>): Effect<A, E>;}