Memo

The EDAG interpreter that remembers: within one call, each shared node is evaluated once and its value reused, so a const read twice is computed once and a shared {} is one object. module.f.mjs holds it.

import { identityAnalysis } from '../analysis/module.f.mjs'
import { unwrap } from '../../types/result/module.f.js'
import { memo } from './module.f.mjs'

// [o, o] with o = {}: one object node, reached twice.
const o = ['{}', []]
const [, value] = memo(unwrap(identityAnalysis(['[]', [o, o]])))({ args: [] })
value[1][0] === value[1][1] // true: one object
ExportDoes
memo(analysis)interprets a whole analyzed graph, given the module's args
invokecalls a represented function value, evaluating its body with a fresh cache
invokeWith(runtime)invoke, with built-in members resolved through runtime

The cache is per call. Each invocation of a function starts an empty cache and keeps its captures by identity, so two calls of one function build two fresh objects, as JavaScript does.

Tail calls run in place. A call in tail position replaces the current invocation, so a function that iterates by calling itself last holds one cache and constant host stack.

It runs the analysis, not the raw graph. analysis's identityAnalysis, the table the compiler gives it, finds the shared nodes once, so the interpreter needs no identity-keyed map of its own.

amnesia is the other interpreter, which forgets, and the demo on fjs/edag's page shows the two side by side. The compiler uses memo to initialize modules, and NaNVM's value tests check against it.

Emergent Testing

Idle. Press Run to start the suite.

  • ./proof.f.mjs