Cache overview
Mutable cache with optional bounded capacity and optional time-to-live built around a user-supplied lookup function. The lookup function may be recursive, in which case the cache is also used to detect circularity.
Mental model
Type<A, B>is a mutable store mapping keys of typeAto values of typeB, populated on demand bylookUp.- Keys are compared with
Equal.equals(the underlying store is aMutableHashMap). - Eviction is FIFO: when capacity is exceeded the oldest insertion is evicted first.
- When
lifeSpanelapses for an entry, the next read triggers a fresh lookup; entries inserted before the expired one are also evicted in the process to keep insertion order consistent with age.
Common tasks
- Construct: {@link make}
- Read: {@link get}, {@link toGetter}
- Inspect: {@link toKeys}
Gotchas
- The cache is mutable —
getmutates the underlying store. Sharing a cache across fibers without external coordination is unsafe. - When the lookup function is recursive, capacity may be temporarily exceeded: every key in the recursion chain is reserved (with an empty entry) before a result is produced, so a chain of
nrecursive calls reservesnentries even pastcapacity. Excess entries are reclaimed once the recursion unwinds. - When
isCircularistrue, the value returned bylookUpis never stored, regardless of the second tuple element.
Quickstart
Example (Basic cache usage)
import { Tuple, pipe } from "effect"
import * as MCache from "@parischap/effect-lib/MCache"
const cache = MCache.make({
lookUp: ({ key }: { readonly key: number }) => Tuple.make(key * 2, true)
})
console.log(pipe(cache, MCache.get(5))) // 10
Table of contents
Constructors
make
Builds a new cache backed by lookUp.
- Use to wrap an expensive function in a cache. Keys are compared with
Equal.equals. capacitydefaults toInfinity(unbounded).lifeSpandefaults toInfinity(entries never expire).
Example (Bounded cache with TTL)
import { Tuple } from "effect"
import * as MCache from "@parischap/effect-lib/MCache"
const cache = MCache.make({
lookUp: ({ key }: { readonly key: number }) => Tuple.make(key * 2, true),
capacity: 100,
lifeSpan: 60_000 // 1 minute
})
Signature
export declare const make: <A, B>({
lookUp,
capacity,
lifeSpan
}: {
readonly lookUp: LookUp<A, B>
readonly capacity?: number
readonly lifeSpan?: number
}) => Type<A, B>
Models
LookUp (interface)
Type of the lookup function passed to {@link make}. The lookup receives a record carrying the key to look up, an isCircular flag, and a memoized callback that re-enters the cache recursively when isCircular is false.
- The function returns a
[result, storeInCache]tuple.storeInCacheonly acts as a hint: whenisCircularistrue, the value is never stored regardless ofstoreInCache. - Use the
memoizedparameter inside the lookup body to recurse through the cache instead of calling the lookup directly; this enables circularity detection.
Signature
export interface LookUp<A, B> extends MTypes.OneArgFunction<
| { readonly key: A; readonly memoized: undefined; readonly isCircular: true }
| { readonly key: A; readonly memoized: (a: A) => B; readonly isCircular: false },
MTypes.Pair<B, boolean>
> {}
Type (class)
Type of a cache.
Signature
export declare class Type<A, B> {
private constructor(params: MTypes.Data<Type<A, B>>)
}
make (static method)
Static constructor.
Signature
static make<A, B>(params: MTypes.Data<Type<A, B>>): Type<A, B>
[MData.idSymbol] (method)
Returns the id of this.
Signature
[MData.idSymbol](): string | (() => string)
store (property)
The underlying store. A key mapped to Option.none means a lookup is currently in progress (used to detect circular recursion).
Signature
readonly store: MutableHashMap.MutableHashMap<A, Option.Option<MCacheValueContainer.Type<B>>>
keyListInOrder (property)
The order in which keys were inserted, used to evict the oldest first when the cache reaches its capacity.
Signature
readonly keyListInOrder: MutableList.MutableList<A>
lookUp (property)
The lookup function used to populate the cache.
Signature
readonly lookUp: LookUp<A, B>
capacity (property)
The capacity of the cache. Infinity means unbounded; NaN or a negative value is normalized to 0.
Signature
readonly capacity: number
lifeSpan (property)
The lifespan of cached values, in milliseconds. Infinity means values never expire; NaN or a negative value is normalized to 0. A 0 lifespan means every subsequent lookup considers the entry expired.
Signature
readonly lifeSpan: number
Module markers
moduleTag
Module tag.
Signature
export declare const moduleTag: "@parischap/effect-lib/Cache/"
Utils
get
Reads the value associated with a from self. Triggers a lookup when the key is missing or its entry has expired.
- Keys are compared with
Equal.equals. - When the lookup is already in progress for
a(recursive call),lookUpis invoked withisCircular: trueand the returned value is not stored. - On expiration of an entry, every entry inserted before it is evicted as well to keep the FIFO insertion order in sync with element age.
Example (Reading from the cache)
import { Tuple, pipe } from "effect"
import * as MCache from "@parischap/effect-lib/MCache"
const cache = MCache.make({
lookUp: ({ key }: { readonly key: number }) => Tuple.make(key * 2, true)
})
console.log(pipe(cache, MCache.get(5))) // 10
Signature
export declare const get: <A>(a: A) => <B>(self: Type<A, B>) => B
toGetter
Returns a getter bound to self, suitable for point-free lookups.
- Use when the same cache will be queried by many call sites and you want to capture it once.
- Equivalent to
(a) => MCache.get(a)(self).
Example (Reusable getter)
import { Tuple } from "effect"
import * as MCache from "@parischap/effect-lib/MCache"
const cache = MCache.make({
lookUp: ({ key }: { readonly key: number }) => Tuple.make(key * 2, true)
})
const lookup = MCache.toGetter(cache)
console.log(lookup(5)) // 10
Signature
export declare const toGetter: <A, B>(self: Type<A, B>) => MTypes.OneArgFunction<A, B>
toKeys
Returns the keys whose value is currently present in the cache.
- Keys whose lookup is in progress (i.e. mapped to
Option.none) are excluded. - The order of the returned array reflects the iteration order of the underlying
MutableHashMap, which is implementation-defined.
Example (Listing cached keys)
import { Tuple, pipe } from "effect"
import * as MCache from "@parischap/effect-lib/MCache"
const cache = MCache.make({
lookUp: ({ key }: { readonly key: number }) => Tuple.make(key * 2, true)
})
pipe(cache, MCache.get(1))
pipe(cache, MCache.get(2))
console.log(MCache.toKeys(cache)) // [1, 2]
Signature
export declare const toKeys: <A, B>(self: Type<A, B>) => Array<A>