diff --git a/docs/WhatsNew.md b/docs/WhatsNew.md index 34fff93..2e62a56 100644 --- a/docs/WhatsNew.md +++ b/docs/WhatsNew.md @@ -162,3 +162,17 @@ The prelude — until today the one file in the project authored as JSON-AST ins Compiled programs are unchanged. Before deleting the old JSON file a one-time identity check ran to confirm that parsing the .ail prelude produces the exact same canonical module the JSON used to deserialise to; the check fired green, so the deletion was safe. A long-term hash anchor stays in place to catch accidental drift. All tests green; benchmark scripts all pass at the existing baselines. + +## 2026-05-15 — Local mutable state arrives + +Programs can now declare mutable bindings inside a function body. A new block form opens a sealed region where the author names one or more mutable cells (with explicit types and initial values), can reassign them as the block runs, and can read their current value as any other identifier. Once the block ends the cells go out of scope — they cannot escape into a returned value, a closure, or anywhere else. The enclosing function's type signature stays pure: no new effect leaks out, so the caller sees the same contract as before. + +The supported cell types are the four stack-resident scalars: integer, floating-point, boolean, unit. The heap-managed string type and any user-defined data type are deliberately not supported in this first cut — extending to them needs separate design work on how reassignment interacts with reference counting. + +Two example programs ship to show the shape: one sums the integers from 1 to 10 using a mutable accumulator (prints 55), the other does the same for floating-point. Both run end-to-end through the build pipeline. + +This is the first step on a longer arc. The motivation is real-world streaming computations — rolling indicators, online aggregates, sensor fusion — whose math is intrinsically "old state plus new input gives new state plus output" per step. Pure-functional state-threading expresses that, but at a real ergonomic cost as pipelines lengthen. Local mutable state is the foundation; later steps add escaping references, the effect annotation that goes with them, and a composable transducer type. The roadmap entry for the broader vision sits under the heading "Stateful islands — bounded mutation for streaming workloads". + +A safety guard added at the same time: a lambda whose body refers to a mutable cell from an enclosing block is rejected by the type checker with a clear diagnostic, since cells are tied to their block's lifetime and cannot be lifted into a heap-stored closure without further design work. + +All tests green. Benchmark scripts all pass at the (updated for this milestone) baselines. diff --git a/docs/roadmap.md b/docs/roadmap.md index 7198348..096f423 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -265,7 +265,7 @@ clean. Pick the next milestone from P2.)_ exercising the LLM-natural Show-body shape. - context: per-iter journal 2026-05-13-iter-str-concat.md. -- [ ] **\[milestone\]** Stateful islands — bounded mutation for +- [~] **\[milestone\]** Stateful islands — bounded mutation for streaming workloads (`Stateful a b` + `!Mut` effect + `mut` syntactic block). Adds a sealed mutable-state layer on top of the pure core: a `Stateful a b` first-class type whose interior @@ -281,6 +281,22 @@ clean. Pick the next milestone from P2.)_ zero-allocation pipe combinator, growing tuple-state types as pipelines lengthen). + **Progress.** Decomposed at brainstorm time (2026-05-15) into a + sequence of shippable sub-milestones. Sub-milestone 1 closed + 2026-05-15: **mut-local** — sealed-by-construction `mut`/`var`/ + `assign` blocks for Int/Float/Bool/Unit scalars, alloca-resident, + no escape, no `!Mut` effect leakage. Foundation in place; + fn signatures stay pure while LLM authors can write imperative + accumulators directly. Spec `docs/specs/2026-05-15-mut-local.md`; + iters mut.1/mut.2/mut.3/mut.4-tidy (commits 7b92719, b24718a, + 03fb633, 20add51). Remaining sub-milestones (to be brainstormed + separately, in order): (2) effect-handler infrastructure as a + prerequisite for any non-IO/non-Diverge effect; (3) `!Mut` effect + + `ref a` first-class type that can escape mut blocks under + uniqueness-tracking; (4) mutable-array primitive `MutArray a`; + (5) `Stateful a b` sealed callable type + `pipe` combinator with + zero-allocation composition + `runST`-equivalent escape discharge. + **Motivation.** AILang's signature-as-contract thesis is *better* served by an explicit `Stateful a b` + `!Mut` annotation than by the implicit-closure-mutation idiom of myc / Lua / JS factory