Skip to main content Link Search Menu Expand Document (external link)

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 type A to values of type B, populated on demand by lookUp.
  • Keys are compared with Equal.equals (the underlying store is a MutableHashMap).
  • Eviction is FIFO: when capacity is exceeded the oldest insertion is evicted first.
  • When lifeSpan elapses 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.get call.
  • When isCircular is true, the value returned by lookUp is 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.
  • capacity defaults to Infinity (unbounded).
  • lifeSpan defaults to Infinity (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>

Source

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 if key is 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>
> {}

Source

Type (class)

Type of a cache.

Signature

declare class Type<A, B> {
  private constructor(params: MTypes.Data<Type<A, B>>)
}

Source

make (static method)

Static constructor.

Signature

declare const make: <A, B>(params: MTypes.Data<Type<A, B>>) => Type<A, B>

Source

[MData.idSymbol] (method)

Returns the id of this.

Signature

declare const [MData.idSymbol]: () => string | (() => string)

Source

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>>>

Source

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>

Source

lookUp (property)

The lookup function used to populate the cache.

Signature

readonly lookUp: LookUp<A, B>

Source

capacity (property)

The capacity of the cache. Infinity means unbounded; NaN or a negative value is normalized to 0.

Signature

readonly capacity: number

Source

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

Source

Module markers

moduleTag

Module tag.

Signature

declare const moduleTag: "@parischap/effect-lib/Cache/"

Source

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), lookUp is invoked with isCircular: true and 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

Source

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>

Source

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>

Source