Files
AILang/examples/fieldtest/mut-local_1_factorial.ail
T
Brummel 1faee673f7 fieldtest: mut-local — 6 examples (4 positive + 2 negative probes), 0 bugs / 3 friction / 1 spec_gap
Boss-dispatched fieldtest after audit-mut-local closed. Six AIL
Surface (.ail) fixtures under examples/fieldtest/ exercise the
shipped mut-local surface from a downstream-LLM-author's perspective
(no compiler-source access; DESIGN.md + public examples only).

Positive fixtures all run end-to-end first-try:
- mut-local_1_factorial.ail: straight-line Int accumulator
  unroll (5!), prints 120.
- mut-local_2_classify_temp.ail: nested-if assigns into a Unit-
  typed mut block, prints classification code.
- mut-local_3_horner.ail: Float mut accumulator for polynomial
  evaluation, prints 18.
- mut-local_4_has_small_factor.ail: Bool mut flag via four
  if-then-assign checks, prints true.

Negative probes confirm diagnostics fire as documented:
- mut-local_5_lambda_capture_probe.ail: [mut-var-captured-by-lambda].
- mut-local_6_diag_probe.ail: [mut-var-unsupported-type].

Findings:

[friction] F1 — mut without iteration: the accumulator-over-an-
iteration shape never materializes. Without while/for, the LLM-
author still writes a tail-recursive helper (the very pattern
the milestone Goal said mut would replace). examples/mut_counter.ail
illustrates the degeneration. Routing: planner for a while/for
iteration OR tighten DESIGN.md to name the gap honestly.

[friction] F2 — all four mut-related diagnostics emit their
bracketed [code] twice ('error: [code] fn-name: [code] message').
Mechanical bug: the #[error('[code] ...')] Display attributes I
authored in mut.2/mut.4-tidy include the bracketed prefix in the
message body, and the cli-diag-human formatter adds another from
CheckError::code(). Routing: debug (mechanical message-body
cleanup).

[friction] F3 — no surface form to call a zero-arg fn ('(app f)'
rejected at parse with 'expected at least one argument').
Orthogonal to mut-local but surfaced building the closure-factory
probe. Routing: planner for a small tidy iter.

[spec_gap] F4 — DESIGN.md does not name the 'use a tail-rec
helper instead' workaround for the iteration-over-accumulator
shape that mut alone cannot express. Routing: ratify in DESIGN.md
alongside F1's resolution.

[working] W1/W2/W3 — surface reachable on first read; diagnostics
pinpoint cause (mut-assign-out-of-scope even lists available
vars); composes cleanly with if + lambda-without-capture +
final-expression position.

Spec: docs/specs/2026-05-15-fieldtest-mut-local.md (333 lines).

Refs: docs/specs/2026-05-15-mut-local.md, audit-mut-local close
at 8685e96.
2026-05-15 09:52:43 +02:00

31 lines
1.1 KiB
Plaintext

; Fieldtest mut-local #1 — factorial 5! via straight-line mut updates.
;
; Task: print 5! (= 120) using a `mut` block that names a running
; product and unrolls five multiplications as straight-line statements.
; This is the most direct possible use of mut-local: no helper fn, no
; iteration, just a sequence of in-block updates terminated by reading
; the var. The LLM-author's mental model of "I want a local accumulator"
; maps 1:1 onto the surface here.
;
; Why this fits mut-local's scope: the milestone supplies only sealed
; lexically-scoped mutables, with no `while` or `for`. Straight-line
; unroll is the *only* shape inside one mut block that needs no helper.
;
; Expected stdout: 120
(module mut-local_1_factorial
(fn main
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body
(app print
(mut
(var prod (con Int) 1)
(assign prod (app * prod 1))
(assign prod (app * prod 2))
(assign prod (app * prod 3))
(assign prod (app * prod 4))
(assign prod (app * prod 5))
prod)))))