roadmap: stateful-islands milestone — bounded mutation for streaming workloads

P2 entry: Stateful a b first-class type + !Mut effect + mut syntactic
block, with uniqueness inference at the boundary. Motivated by the
2026-05-15 myc-vs-AILang analysis showing AILang's pure-only model
cannot match the stateful-closure-plus-pipe idiom for online /
streaming workloads (rolling indicators, IIR filters, online stats).

The signature-as-contract thesis is *better* served by explicit
Stateful + !Mut annotations than by myc's implicit closure-mutation:
time-identity becomes visible in signatures without body inspection.
Decision 10's no-shared-mutable-refs forbids aliased mutation; it
does not forbid uniqueness-bounded mutation, which is the Lean 4 /
Roc / Haskell-ST / Koka layered-design pattern.

Ten blockers identified, from effect-handler infrastructure (the
first non-IO/non-Diverge effect) through uniqueness inference for
var captures, mutable-array primitive, runST-equivalent escape
discharge, Form-A surface design, codegen for mutable struct-field
writes, escape analysis for var/ref values, Decision-10 amendment
vs. Decision 12, streaming bench corpus, and LLM-utility fieldtest.
This commit is contained in:
2026-05-15 00:26:16 +02:00
parent cf85282693
commit ae96c903a1
+111
View File
@@ -265,6 +265,117 @@ 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
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
is mutable, whose exterior is a typed callable with `!Mut` in
its effect set, and whose state non-aliasing is enforced by
uniqueness inference at the block boundary. Targets the
online / streaming workload class — rolling indicators, IIR
filters, online aggregates, sensor fusion, online learning —
whose mathematics is "new sample + old state → new state +
output" per step, and which today's pure-functional state-
threading makes ergonomically expensive at scale (multi-record
explicit threading for the canonical sliding-window SMA, no
zero-allocation pipe combinator, growing tuple-state types as
pipelines lengthen).
**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
patterns: the signature tells the truth about time-identity
without the body needing to be read — exactly the LLM-author
correctness affordance AILang exists to deliver, extended to a
workload class it does not yet serve. Decision 10's constraint #3
forbids *shared* mutable refs (DESIGN.md:1082); it does not
forbid uniqueness-bounded mutation. Lean 4 (`ST`), Roc (`Task`),
Haskell (`ST`/`IO`), and Koka (effects) all use a layered design
of this shape to host exactly this workload. AILang's existing
`own` / `borrow` mode machinery plus its effect-slot architecture
are the foundation; this milestone supplies the layer that sits
on top.
**Scope (subject to brainstorm refinement).**
- `Stateful a b` as a first-class type — a sealed callable with a
hidden mutable env, externally a typed callable whose effect
set includes `!Mut`.
- `!Mut` effect, the first non-IO / non-Diverge effect; forces
the effect-handler infrastructure forward.
- `mut` block as the syntactic boundary in Form A — outside,
AILang's pure self; inside, `var` / `assign` / mutable arrays /
while-loops legal.
- `var x = expr` + `assign x expr` AST nodes, legal only inside
`mut`.
- Mutable array primitive (`MutArray a`, O(1) index/update under
uniqueness) — co-developed because the ring-buffer use case
that motivates the whole effort has no carrier today.
- `pipe : Stateful a b → Stateful b c → Stateful a c` as a
built-in combinator with zero-allocation lowering — composition
is fusion at codegen, not closure-pair layering at runtime.
- Decision 12 (or amendment to Decision 10) that names
uniqueness-bounded mutation as the legitimate exception to
"no shared mutable refs", and the pure / mutable-island layering
as the architectural shape.
**Blockers (hard prerequisites + open design questions).**
- *Effect handlers absent.* DESIGN.md:2663 — "No effect
handlers — only the built-in IO and Diverge ops." `!Mut` is the
first non-trivial effect and forces handler infrastructure to
ship. May warrant lifting out as its own prerequisite milestone;
brainstorm decides.
- *Uniqueness inference through `var` captures.* Current
inference operates over the immutable RC graph; mutable
bindings captured by closures need a more powerful pass. Lean 4
has a known algorithm; AILang does not implement it.
- *No mutable-array primitive.* No `Array a`, no slab container
of any kind in the language today. Separate spec needed inside
this milestone (representation, `own`-only or `borrow`-able,
bounds-checking discipline).
- *`runST`-equivalent escape discharge.* Producing a `Stateful`
that legitimately escapes its `mut` block while proving the
inner state doesn't leak. Haskell's rank-2 trick
(`runST :: (forall s. ST s a) -> a`) is unavailable —
DESIGN.md:1930 confirms higher-rank polymorphism is not
supported. Need an alternative — likely sealed-by-construction
at the factory boundary, or an effect-mode tag that gates
escape.
- *Form A surface design.* Decision 1 forbids macros (source =
data, not text), so `mut` / `var` / `assign` are genuine AST
nodes with round-trip-invariant coverage. Surface needs LLM-
utility validation (does the author reach for `var` / `mut`
unprompted?) before implementation.
- *Codegen for in-place struct-field writes.* `Term::ReuseAs`
today lowers ADT in-place rewrite; no direct mutable struct-
field-write path exists. `crates/ailang-codegen/src/escape.rs`
plus the lowering passes need extension.
- *Boundary escape analysis.* "Interior state doesn't leak" is an
escape analysis specifically over `var` / `ref` values; the
existing pass covers allocations, not mutable cells.
- *Decision-10 status.* Whether to amend Decision 10 inline or
add a Decision 12 that names the mutable-island layer alongside
the pure layer. The latter is probably cleaner — Decision 10
stays load-bearing for the pure layer, Decision 12 names the
extension and its discipline.
- *Streaming bench corpus.* Current corpus (list_sum, tree_walk,
closure_chain, hof_pipeline) is all pure. Streaming-specific
benches (SMA pipeline, IIR filter, online stats) plus an
external baseline (myc, hand-C, Python/NumPy) need to exist to
validate that the layered design hits the zero-alloc
performance target that myc demonstrates.
- *LLM-utility test.* DESIGN.md §"Feature-acceptance criterion"
is the gate: the surface must produce code an LLM author
naturally writes. Fieldtest after spec, before any code
commits.
- context: 2026-05-15 chat on the `~/sma_factory.myc` analysis —
myc's stateful-closure-plus-pipe idiom delivers a streaming
workload pattern (3-line SMA factory, 2-line pipeline, zero
runtime allocation per tick) that AILang's pure-only model
cannot match without this layered extension. Pending brainstorm
will produce `docs/specs/<date>-stateful-islands.md` and decide
the effect-handler-prerequisite question.
## P3 — Ideas
- [x] **\[todo\]** `compare_primitives_smoke.ail` counterpart.