Skip to main content
Deno 2 is finally here 🎉️
Learn more

memo

JSR codecov GitHub semantic-release: angular standard-readme compliant

Memoization tools, TC39 proposal-function-memo implementation.

Table of Contents

Install

deno:

deno add @miyauci/memo

node:

npx jsr add @miyauci/memo

Usage

Returns the proxy function whose call is monitored. It calls at most once for each given arguments.

import { memo } from "@miyauci/memo";

function f(x: number): number {
  console.log(x);
  return x * 2;
}

const fMemo = memo(f);
fMemo(3); // Prints 3 and returns 6.
fMemo(3); // Does not print anything. Returns 6.
fMemo(2); // Prints 2 and returns 4.
fMemo(2); // Does not print anything. Returns 4.
fMemo(3); // Does not print anything. Returns 6.

Either version would work with recursive functions:

import { memo } from "@miyauci/memo";

const fib = memo((num: number): number => {
  if (num < 2) return num;

  return fib(num - 1) + fib(num - 2);
});

fib(1000);

Custom cache

To control the cache, specify cache.

The cache must implement the following interfaces:

interface MapLike<K, V> {
  get(key: K): V | undefined;
  has(key: K): boolean;
  set(key: K, value: V): void;
}

By default, an unlimited cache is used by WeakMap.

import { type MapLike, memo } from "@miyauci/memo";

declare const lruCache: MapLike<object, unknown>;
declare const fn: () => unknown;

const $fn = memo(fn, lruCache);

See TC39 proposal-policy-map-set and its implementation.

Keying

Cache keys are represented by composite keys.

The composite keys are passed several elements for the key, called components.

The default components are as follows:

  • this arg(this)
  • args

Also, composite key employs the same-value-zero algorithm to verify the equivalence of each component.

You can modify the component through the keying callback.

import { type MapLike, memo } from "@miyauci/memo";

declare const respond: (request: Request) => Response;

const $respond = memo(
  respond,
  undefined,
  (thisArg, [request]) => [request.method, request.url.toString()],
);

Construction caching

Caching of construction is also supported. Calls to constructor functions with the new operator are cacheable based on their arguments.

import { memo } from "@miyauci/memo";
import { assert } from "@std/assert";

declare const url: string;

assert(new URL(url) !== new URL(url));

const $URL = memo(URL);

assert(new $URL(url) === new $URL(url));

Keying for construction

Unlike in the case of functions, the following components are used as keys by default in the construction:

  • args
  • newTarget(new.target)

You can specify keying as in the function.

import { type MapLike, memo } from "@miyauci/memo";

const $URL = memo(
  URL,
  undefined,
  (
    thisArg,
    [url, base],
    newTarget,
  ) => [url.toString(), base?.toString(), newTarget],
);

Polyfill

Polyfill affects the global object. You must be very careful when using it.

import "@miyauci/memo/polyfill";

const fib = ((num: number): number => {
  if (num < 2) return num;

  return fib(num - 1) + fib(num - 2);
}).memo();

fib(1000);

API

See jsr doc for all APIs.

Contributing

See CONTRIBUTING.md

License

MIT © 2023 Tomoki Miyauchi