Architecture
A whole program is just a sequence of calls on one object, Reactive. Every call forwards to the
current interpreter; swapping the interpreter reinterprets the very same program: run it,
type-check it, or generate code from it. Keep the calls pure and the meaning is decided entirely by which
interpreter is installed. That is the whole design.
Reactive is the substrate
Every other module, Content, Layout,
Button, and the rest, is a reified module: it registers itself by calling
Reactive.Module / Function / Type. Reactive gives reified code its
meaning, so reifying it through itself would be circular; it is hand-written and special by design. It still
swaps its own behaviour directly, by flipping the current interpreter.
Reactive._core // the interpreters, keyed by name (define, run, …)
Reactive._mode // the active interpreter's name
Reactive._current // the active interpreter object
One op, one forwarder
Each reified operation is a one-line forwarder to the current interpreter, so an interpreter only implements what it supports and an unsupported call fails loudly rather than doing the wrong thing silently:
Reactive.State = function () {
return Reactive._current.State.apply(Reactive._current, arguments);
};
The one primitive: save · swap · restore
Switching interpreter, replacing a module for a subtree (the WITH idea), setting the current
context, and the lock-discovery pass are all the same move: temporarily change what's installed, run a
callback, put it back. The update loop is fully synchronous, so nothing can interleave and a plain
save/restore is enough:
Reactive._inMode("run", () => { /* … executes under the run interpreter … */ });
The interpreters
| Mode | What it does |
|---|---|
define | Include-time. Module/Function/Type register structure; function bodies are stored, not run. |
run | Execution. The real value/lock/gather machinery, and the update loop that drives the DOM. |
discover | A dry pass for a locked If/Each: records which contexts a callback could reach, all branches, no side effects. |
typecheck | Walks types and inputs and asserts; executes nothing. Runs before mount. (planned) |
codegen | Each op emits target source rather than executing it: JavaScript, a shader, a document. (planned) |
define and run, the full runtime behind every
live example, including the scope tree, If/Each, locks,
loading and gather. The remaining interpreters (typecheck, codegen) are still scaffolded; the point of the
architecture is that they are additive, never a rewrite.Why this shape
This is the tagless-final pattern: a language expressed as calls on an interface rather than as a data structure to walk. It gives the same multi-target power a compiler's AST would (run / check / generate) while the program is the calls, sparing you an AST to build. Small runtime, high ceiling.