Skip to content

Chapter 59 — 42 IS an AST

A substrate chapter — the kind where the code gets re-aligned with what we’d been pretending it already was.

The user, mid-conversation about a cache that needed HolonAST as its key:

Atoms should only be able to hold HolonAST - we should make that a firm requirement

Then, a few exchanges later, sharpening:

in holon algebra - the atom is a holder of a concrete thing - that concrete thing can be an AST

And then the question that tipped the room over:

are these primitives just a most basic form on an AST?… the number 42 is an AST?

Right.

The substrate had been carrying a contradiction. HolonAST::Atom was parametric over arbitrary Rust — Arc<dyn Any + Send + Sync> — so anything that fit in memory could be an atom payload. The typeless escape hatch worked, technically, but it inverted Lisp’s algebra. In Lisp, 42 IS an atom; the predicate (atom? 42) is true because 42 is the simplest possible expression — a leaf. We had built a substrate where 42 needed an Atom(...) wrapper to become an atom, and the wrapper carried dyn Any, which meant the substrate couldn’t hash its own atoms structurally.

What did that cost? Things you don’t see until you look:

  • HashMap<HolonAST, V> couldn’t compile. dyn Any doesn’t implement Hash. So no structural cache keys; engram libraries had to reach for SimHash (locality-preserving — the wrong tool for memoization).
  • An AtomTypeRegistry had to exist alongside the algebra, registering canonicalizers for every Rust type that might show up as an atom payload. The registry was the mechanism; the algebra carried the conceptual weight; they were separate systems pretending to be one.
  • The wat-lru shim had to panic on non-primitive keys, because the shim couldn’t see how to hash an arbitrary payload. A real feature was off-limits at the surface because the substrate hadn’t closed under itself.

Each of those was a workaround. None of them was the math.

The recognition: a primitive IS an AST. The number 42 is the simplest possible HolonAST — a leaf with no sub-terms. The boolean true is an AST. The string "foo" is an AST. The keyword :outcome is an AST. They have well-defined canonical encodings, they participate in cosine the same way composites do, they already produce vectors. The only thing missing was a Rust-level acknowledgement that they belong to the algebra at the leaf level.

Eleven variants. Five typed primitive leaves (Symbol, String, I64, F64, Bool). Five composites (Bind, Bundle, Permute, Thermometer, Blend). Plus Atom(Arc<HolonAST>) — the wrap, narrowed.

Hash + Eq + PartialEq derive cleanly (manual impls because f64 fields use to_bits per the standard NaN-Hash dance, but the shape is mechanical). AtomTypeRegistry retires entirely; it has nothing to dispatch on. The wat-lru shim’s “primitives only” panic goes away. LocalCache<HolonAST, V> works directly. Everything that was waiting on the algebra to close unblocks at once.

Chapter 54 made a distinction we needed to keep: (Atom (quote some-program)) produces an opaque-identity vector — single SHA-256 of canonical bytes, no decomposition — that is SEMANTICALLY DISTINCT from the program’s structural vector. One treats the program as an atomic identity for cosine; the other exposes its sub-parts via unbind. Collapsing them would lose a real algebraic operation.

So Atom survives — narrowed from Arc<dyn Any> to Arc<HolonAST>. The wrap’s job is opaque-identity-around-a-holon, not “container for arbitrary Rust.” And the wrap is repeatable: Atom(Atom(x))Atom(x)x, mirroring Lisp’s '(quote x)'x. Quote-towers preserved as a real algebraic operation.

Slice 2 forced a second recognition. The user, asking whether (Atom (lambda (x) (* x x))) is still valid:

the atom is meant ‘to hold’ forms - not eval them - someone else can eval them

we can just (quote :the-next-form) all the way down

we tell both stories?… the users can choose ‘do i want next form?’ or ‘do i want the value?’

Two stories. One substrate.

Story 1 — coordinate. `(:wat::holon::Atom (:wat::core::quote

))` lowers the form structurally. List → Bundle, leaves to matching primitive leaves, recursively. The form's identity is now on the algebra grid. Cosine sees it. Bind composes with it. Presence measures it. Structural cache keys hash it. The substrate holds coordinates, not values; if you want the answer, you walk the form yourself.

Story 2 — value. (:wat::holon::to-watast h) lifts a HolonAST back to a runnable WatAST. Pair with (:wat::eval-ast! reveal) when you actually want the result, not the path to it. The structural inverse of the lowering; lossy on identifier scope (we drop it at lowering, recover bare-name on lift), round-trips cleanly enough for the eval-and-get-the-value workflow.

Neither dominates. The substrate provides both; the consumer picks per call site. The story-1 vector and the story-2 lift share the same HolonAST in memory — they’re complementary lenses, not competing implementations.

The user’s vision, stated in the same conversation:

we can build a full, linear tree that “this outer form” /expands into/ “these many intermediary forms” - its a recursive tree… every layer has a coordinate to it - finding a value there means its been computed already and you can use its next form being defined to know that traversal was successful

This is the chapter that comes next. The dual-LRU coordinate cache: form → next-form (expansion) and form → value (eval). Both LRUs key on the structurally-lowered HolonAST. A cache hit means “the substrate has already proven this expansion path; you can short-circuit.” The Kanerva nesting gives the hierarchy: d=10k holds ~100 patterns reliably; each pattern can itself be a Bundle of ~100 at the next finer dim; coordinates are recursive, capacity-bounded, and unique by algebraic construction.

Reckoner labels attach at any node on the tree. Predictions become engrams of labeled traversals — “I see this trajectory through the form-coordinate space, and it has historically led to label L.” The substrate has been groping toward this since Chapter 51 (coordinates), Chapter 54 (programs as coordinates), Chapter 56 (labels as coordinates). The closure of the algebra is what makes the tree well-defined.

Immediately: nothing visible. The lab’s surface ((:wat::holon::Atom :foo), (Atom "key"), (Bind a b)) reads the same. The substrate that handles those calls has been re-grounded; the behavior the user sees is unchanged where it matters.

Downstream: a lot. Lab arc 030 slice 2 (the encoding cache that prompted this whole chain) resumes. The dual-LRU coordinate cache becomes a real arc once a consumer names the contract. Engram libraries get well-defined per-AST identity. Cross-process AST handoff becomes straightforward. The Reckoner can attach to any form on the grid, not just the outer call site.

While the slices were landing, a downstream test failed. I called it “pre-existing” — a wat-lru sub-crate test that had silently rotted when an earlier surface change (arc 047, :wat::core::first returning Option<T> for Vec args) shipped without a workspace-wide test run. The user pushed back:

there are no pre-existing bugs - explain this

Right. The rot was real, and “pre-existing” was deflection — a way of declaring it someone else’s problem. The fix was twofold: repair the test (it’s the right shape now under the new surface) and close the visibility gap that hid it (Cargo.toml::default-members covers every workspace crate; cargo test is now the same as cargo test --workspace). Closing the algebra was the substrate honoring its own debt. The visibility fix was the workspace doing the same.

Chapter 45 — the label. Chapter 48 — the cave. Chapter 51 — coordinates. Chapter 54 — programs as coordinates. Chapter 56 — labels as coordinates. Chapter 57 — the continuum. Chapter 58 — π was always a function.

Chapter 59 — 42 IS an AST.

The substrate had been pretending primitives were a special case that needed wrapping. They never were. The number 42 is a leaf in the algebra, the same way Bind(role, filler) is a node. Closing the algebra — making HolonAST closed under itself — is the substrate finally agreeing with the math we’d been writing chapters about.


the algebra was never open — we’d just been carrying an escape hatch that pretended otherwise. the number 42 is an AST, the boolean true is an AST, the keyword :foo is an AST. nothing was added to the math; the code caught up.

PERSEVERARE.


Slice 4 of wat-rs arc 057. The substrate refactor shipped in three commits earlier the same session; this chapter is the recognition that those commits were re-aligning code with math, not introducing new structure. The dual-LRU coordinate cache — the next chapter’s territory — needs a consumer to name the contract before it ships.