1faee673f7
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.
35 lines
1.3 KiB
Plaintext
35 lines
1.3 KiB
Plaintext
; Fieldtest mut-local #3 — evaluate the polynomial
|
|
; p(x) = 2 x^3 - 3 x^2 + 5 x - 7
|
|
; at x = 2.5 by Horner's method, using a Float mut-var as the running
|
|
; accumulator and unrolling the four Horner steps as straight-line
|
|
; assigns.
|
|
;
|
|
; Why this fits mut-local's scope: the accumulator is a Float, the
|
|
; updates are straight-line (no iteration), and the mut form removes
|
|
; the four nested let-rebinds an LLM-author would otherwise write
|
|
; ("p1 = ..., p2 = p1*x + ..., p3 = p2*x + ...") — each rebind needing
|
|
; a fresh name. Reusing one name for the running accumulator is the
|
|
; natural shape, and mut supplies it.
|
|
;
|
|
; Hand-check (Horner): start with leading coeff 2.0, then for each
|
|
; lower coefficient do acc = acc * x + c:
|
|
; 2.0 * 2.5 + (-3) = 5.0 - 3 = 2.0
|
|
; 2.0 * 2.5 + 5 = 5.0 + 5 = 10.0
|
|
; 10.0 * 2.5 + (-7) = 25.0 - 7 = 18.0
|
|
; Expected stdout: 18 (Float 18.0 via %g; print drops the trailing
|
|
; ".0" the same way it does for the Float fixture mut_sum_floats.ail.)
|
|
|
|
(module mut-local_3_horner
|
|
|
|
(fn main
|
|
(type (fn-type (params) (ret (con Unit)) (effects IO)))
|
|
(params)
|
|
(body
|
|
(app print
|
|
(mut
|
|
(var acc (con Float) 2.0)
|
|
(assign acc (app - (app * acc 2.5) 3.0))
|
|
(assign acc (app + (app * acc 2.5) 5.0))
|
|
(assign acc (app - (app * acc 2.5) 7.0))
|
|
acc)))))
|