This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.

Tree

import std/tree

Koru Standard Library: Tree — owned heap nodes, `.k`-spellable

tree.kz · 4 tors

Koru Standard Library: Tree — owned heap nodes, `.k`-spellable · 48 more lines
Koru Standard Library: Tree — owned heap nodes, `.k`-spellable `std/tree:node(Node) { fields }` declares a heap-node KIND in the shared proto registry (kind=node); `std/tree:mint` / `std/tree:dispose` are the value verbs. An edge field is OWNERSHIP: one parent per node, dispose is a recursive free, and a node's lifetime is one interval rooted at a binding. Graphs refuse — shared edges need refcounts or explicit unshare verbs, which is a different lane (`std/graph`), not a stretched tree. SURFACE: std/tree:node(Node) { item: i64, left: ?Node, right: ?Node } std/tree:mint(Node) { item: v, left: l, right: r } -> *Node<owned!> std/tree:dispose(t) Field grammar is proto's `name: Type`; the field type's REGISTRY KIND decides its role — the disambiguation is nominal, never syntactic: scalar / proto-terminal / proto-compound → payload (embedded; compounds expand inline — the acyclic rule is for embedding, not pointing) node-registered (incl. Self) → edge (materialized as a reference; cycles legal) ?T → optional (`param: ?Type` convention — a `?` payload may be absent, a `?` edge may be null or omitted at mint) Zig materialization: payload fields embed their material; edge fields are `?*T` (optional) or `*T` (required — the node is never a leaf, which is what "required child" means). Same name registered as proto AND as node is a collision at declaration — one type namespace per home (the class/struct rule); the checker's declared-type scan owns that wall. The lane keeps mint/dispose honest by leaning on the obligation checker: `mint-Node`'s edge params are `?*T<!owned>` (consumes the child's debt into the node) and its return is `*Node<owned!>` — an edge arg must be a fresh mint or `null` because a second consume of an owned binding is already a refusal. `dispose-Node(t: ?*Node<!owned>)` settles the root debt; the children's debts moved inside it at mint, so the recursive Zig free is the whole story — one interval, one settle. v1 CUTS (loud refusals, not gaps): payload fields are scalar/terminal/ `ref(T)`/owned-leaf only — proto-compound payload embeds are refused pending a decided spelling for how they lower; `walk`/`fold` traversal verbs are a later rung (a plain recursive tor is the honest spelling meanwhile — see the binarytrees port).

~part registry koru_std/tree.registry.kz

Koru Standard Library: the `registry` part of tree.kz. Joined via `~part registry` in tree.kz; merged into the tree module. Node registrations read two places, exactly like proto's own doors: a LIVE `std/tree:node` invocation (declaration still in the tree — the transform may not have visited it yet) and the erased `// proto-node Name: fields` marker it leaves behind. A reader that checks only one ordering-mines the registry; both scans answer "is X a node here". Field CLASSIFICATION is the lane's whole grammar. `name: Type` splits on `?` (optional) and on which registry `Type` lives in: node-registered or the decl's own name → edge scalar / terminal / proto-compound → payload anything else → refusal (caller words it)

~part node koru_std/tree.node.kz

Koru Standard Library: the `node` part of tree.kz. Joined via `~part node` in tree.kz; merged into the tree module. `std/tree:node(Node) { fields }` — the KIND declaration. Registers Node in the shared registry as kind=node, validates the field grammar (payload vs edge by registry kind of the field type), and materializes the host node record. Nothing else emits here — mint/dispose synthesize their own tors lazily at their call sites, so transform order is never a contract.

node

comptimetransform koru_std/tree.node.kz:10

~part mint koru_std/tree.mint.kz

Koru Standard Library: the `mint` part of tree.kz. Joined via `~part mint` in tree.kz; merged into the tree module. `std/tree:mint(Node) { item: v, left: l, right: r }` — create one node. The transform validates the arg block against Node's registered fields (required edges and payloads must be spelled; `?` fields may be omitted or null), synthesizes `mint-Node` into the caller's module on first use (dispatch-gap doctrine: the concrete per-type tor is synthesized, the lane verb routes), and rewrites the site to call it. The single-owner law is not a check here — it is the signature: edge params are `?*T<!owned>` / `*T<!owned>`, so an edge arg must be a fresh mint or `null`; re-using an already-bound child is a second consume of an owned binding, which the obligation checker already refuses.

mint

comptimetransform koru_std/tree.mint.kz:16

~part dispose koru_std/tree.dispose.kz

Koru Standard Library: the `dispose` part of tree.kz. Joined via `~part dispose` in tree.kz; merged into the tree module. `std/tree:dispose(t)` — consume the `owned!` root, recursive free. The transform resolves WHICH node type `t` is (the enclosing tor's param signature, or the in-flow invocation that bound it), synthesizes `dispose-<Node>` + the module-scope `__koru_tree_free_<Node>` walker on first use, and rewrites the site to call it. The obligation checker owns "consumed twice" and "never disposed" — this transform only routes.

dispose

comptimetransform koru_std/tree.dispose.kz:11

~part fold koru_std/tree.fold.kz

Koru Standard Library: the `fold` part of tree.kz. Joined via `~part fold` in tree.kz; merged into the tree module. `std/tree:fold(t) | node n -> <expr> | null -> <expr>` — the catamorphism: a pure, value-returning recursive collapse of an owned tree. Inside `| node n`, a payload field reads as payload and an EDGE field reads as the already-folded child — the field's registry kind flips its meaning in fold position, the same law that makes `node`'s field grammar decide payload vs edge. `| null` is the empty case, required always (a `?` edge reaches it on every leaf; a `?*Node` root reaches it on an absent tree). The transform resolves `t`'s node kind by provenance (dispose's resolveNodeTypeOf — param signature or in-flow producer), rewrites the site's node-arm expression at text level (`n.<payload>` → `n.<f>` on the unwrapped pointer, `n.<edge>` → the recursive fold call), synthesizes `__koru_tree_fold_<Node>_<h> { t: ?*Node<*> } -> T` as an event + |zig proc (`if (t) |n| return <arm>; else return <base>;`), and replaces the site flow with a plain call — the arms are CONSUMED by the rewrite, the `: c` bind and the chain tail survive untouched. fold is the value-side lane verb for the invariant hoist: `build |> fold |> dispose` in a `for ! each` body reads as one invariant prefix — fold reads a hoisted name, returns a value, stays clean by lane contract (the frontend pass classifies it before transforms expand, same door `dispose` walks through as a consume). v1 cuts (loud refusals): single-kind folds only — every edge must reach the same node kind (heterogeneous folds need per-kind arms, undecided); arms are single `-> expr` terminals, not step chains; the node binding may only read declared fields (a bare `n` in the expression refuses).

fold

comptimetransform koru_std/tree.fold.kz:32