Cache.ts 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 will detect circularity. In that case, all intermediate outputs of the lookup function are cached too (e.g. if the lookup function is the factorial function and you call MCache.get(3), results for MCache.get(2) and MCache.get(1) will be stored in the cache too).
Note: If you use this cache with a lookup function that returns an Effect, it’s the Effect itself that gets cached, not its result (this is seldom what you want to do). In that case prefer using Cache module from effect package.
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:
make - Read:
get,toGetter - Inspect:
toKeys
Gotchas
- To be able to track cycles of recursive lookup functions, the cache size may exceed capacity during the duration of an
MCache.getcall. - 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
See
make— create a new cache
Exports Grouped by Category
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
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 make. lookUp takes three parameters:
key: the value for which the function must return a result.isCircular: a flag indicating ifkeyis already under calculation, meaning the lookup function has a cycle.memoized: the function to call if the function you’re defining needs to call itself recursively (a memoized version of the lookup function).
memoized is only present if isCircular is false (to prevent calling memoize again, which would trigger an endless cycle).
For instance, you call MCache.get(2), which triggers the following calls:
lookup({key:2, isCircular:false, memoized})memoized(3)lookup({key:3, isCircular:false, memoized})memoized(2)lookup({key:2, isCircular:true})
In addition to the result, the lookup function must return a storeInCache flag that indicates if the result is to be stored in the cache (usually true). However, if isCircular is true, storeInCache is ignored and the result is not stored in the cache.
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
declare class Type<A, B> {
private constructor(params: MTypes.Data<Type<A, B>>)
}
make (static method)
Static constructor.
Signature
declare const make: <A, B>(params: MTypes.Data<Type<A, B>>) => Type<A, B>
[MData.idSymbol] (method)
Returns the id of this.
Signature
declare const [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
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
See
toGetter— return a reusable getter bound to a cache
Signature
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
See
get— single-shot read
Signature
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
declare const toKeys: <A, B>(self: Type<A, B>) => Array<A>