Every language answers "who frees this?" somewhere. C answers it in the programmer's head, garbage-collected languages answer it later and invisibly, and M9 answers it in the signature: storage is carved from a named POOL, a pool is freed as one act, and a procedure that keeps memory beyond its own frame takes the pool as a parameter. Measured across this repository's whole library, the rule holds with no exceptions worth naming: *the pool parameter appears exactly when the allocation outlives the call.* A signature with a pool tells you the result lives on and who owns it; a signature without one is a promise that nothing was kept.
all: n=8 mean=5.25 spread=10.50
mid: n=4 mean=5.25 spread=4.50
shifted: n=5 mean=102.25 spread=4.00
pools: carve, use, free as one
30 characters, and every one accounted for
Walk the pieces:
VAR pool : POOL declares an arena. As a program-level variable it dies with the program; as a procedure local (scratch : POOL) it dies with the frame, which is the honest spelling of "temporary". There is no per-object free — freeing the pool frees every string, slice and record carved from it, in one act that cannot miss one.
SLICE OF F64 is a view: a pointer and a length, no copy. An ARRAY lends itself as a slice at a call site, SLICE (a, 2, 4) takes a checked sub-view of it, and NEW (pool, F64, 5) carves a slice with no array behind it. Every access through any of them is bounds-checked against the slice's own length — chapter 1's founding rule, applied to views.
RO and VAR are the lending terms.RO xs says *read only, provably*; VAR xs says *this procedure writes through the slice*, visible at both ends. Shift (more, 0.25) announces the mutation at the call site, and without VAR the checker refuses the write inside.
Strings are SLICE OF CHAR — the predeclared name STR is exactly that. Literals take ' or " with no escapes (a string cannot contain its own delimiter, and nothing in a string is ever secretly something else). + composes strings into HEAP, the one predeclared, never-freed pool: fine in a program whose strings die with it, wrong in a server loop, and greppable either way. For ACCUMULATION — building a string in a loop — DynStr grows a buffer in a pool you name; s := s + x in a loop copies the whole string every pass, and chapter 1's museum spirit says: make the cost visible, then choose.
The docstrings are load-bearing. The comment under each procedure header, with its name -- description parameter lines, is what m9c --doc renders into the module's reference page and what the editor shows on hover (chapter 3). These examples carry them from here on — documentation that lives next to the signature is the only kind the compiler can keep honest.
What the checker refuses
The classic C bug in this territory is returning a pointer into a dead stack frame. M9's version is a pointer into a dead POOL — and it does not compile:
24:3 X4Escape.Make: pool-interior pointer escapes its pool: p lives in scratch, which dies with this frame (par 4.3)
The type PTR Point IN scratch names the pool the pointer lives in, so "does this outlive its arena?" is a question the checker can answer — and does, at compile time, with the frame and the pool in the message. The fix is the signature saying where the storage should live instead: give Make a VAR pool : POOL parameter, declare p : PTR Point IN pool, and the same program compiles and runs — the caller now owns the point, exactly as in Describe above. (Try it in the cell: it is a three-line edit. Note the allocation spelling while you are there: NEW (pool, Point), pool first, like every NEW.)
Ownership goes further than this chapter needs — SHARED counted handles and OWN moves appear with the zarr store in chapter 8 — but the rule of thumb carries the whole way: the signature says who owns what, and the checker holds everyone to it.