Files
AILang/examples/fieldtest/eqord_1_fizzbuzz.ail
T
Brummel 505eb8484e fieldtest: operator-routing-eq-ord — 5 examples, 6 findings (4 working, 1 friction, 1 spec_gap)
Post-audit field test of the operator-routing-eq-ord milestone
(closed via 5170b6a + 4d45bc6). Five `.ail` Surface-form fixtures
under `examples/fieldtest/` written by an LLM-author working
strictly from the design/ ledger + public examples (no
`crates/` / `runtime/` / `bench/` reads — Iron Law honoured):

  eqord_1_fizzbuzz.ail        — FizzBuzz [1..15] via (app eq …)
                                 on Int+Str and (app gt …) on Int
  eqord_2_rational_eq.ail     — data Rational + user-instance
                                 (class prelude.Eq) by cross-mult;
                                 inner Int-eq nested in instance body
  eqord_3_newton_sqrt.ail     — Newton's method via float_lt
                                 (convergence + fabs) and float_eq
                                 (zero-guard)
  eqord_4_float_ord_must_fail.ail — (app lt 1.5 2.5) must reject
                                     at typecheck with float_lt hint
  eqord_5_float_eq_must_fail.ail  — (app eq 1.5 1.5) must reject
                                     at typecheck with float_eq hint

Findings:

  [working] x4 — primitive Eq/Ord at Int/Bool/Str/Unit;
    user-ADT Eq with nested primitive eq calls; Float named-fn
    surface (float_eq/float_lt/etc.); NoInstance Eq/Ord Float
    diagnostic with float_eq / float_lt addendum. The milestone's
    central thesis (class-dispatch is the only comparison
    surface; Float opts out via named fns) is LLM-natural —
    every (app eq …) / (app gt …) / (app float_lt …) call I
    wrote compiled on first try with no diagnostic friction.

  [spec_gap] x1 — `docs/specs/2026-05-20-operator-routing-eq-ord.md:113`
    north-star example writes bare `(class Eq)` where the
    language requires `(class prelude.Eq)`. An LLM-author who
    copies the spec verbatim hits `bare-cross-module-class-ref`.
    The milestone spec is the highest-information reference an
    LLM-author consults; the bare-vs-qualified drift mis-primes
    the pattern. Resolution: tighten-the-design-ledger — fix
    the spec example inline (separate follow-up commit). The
    existing `examples/eq_ord_user_adt.ail` already uses the
    qualified form, so the corpus is consistent; only the spec
    drifted.

  [friction] x1 — `(app compare 1.5 2.5)` at Float fires the
    same diagnostic as `(app lt …)` at Float, naming
    `float_eq / float_lt (and siblings)` as the alternative.
    But the alternatives don't return Ordering; an LLM-author
    who wanted three-way LT/EQ/GT can't satisfy that with
    float_lt + float_eq alone. The spec (lines 433-436)
    acknowledged this case in commentary — "no float_compare
    ships; build it from float_lt + float_eq if you need
    three-way" — but the diagnostic doesn't say so. Resolution:
    file as Gitea backlog issue for a follow-up tidy iteration
    (codegen-side: branch the Float-aware NoInstance addendum
    on the called method-name; the `compare` arm gets a
    one-sentence "no float_compare; build it from float_lt +
    float_eq if you need three-way" addendum).

Status: clean — no bugs, no blockers, the milestone surface is
solidly LLM-usable. Both non-working findings are tractable
forward-fixes that don't disturb the milestone's core
contracts.

Spec: docs/specs/2026-05-21-fieldtest-operator-routing-eq-ord.md
2026-05-21 01:34:19 +02:00

74 lines
1.7 KiB
Plaintext

; Fieldtest fixture — Axis 1: equality on primitive Int via `(app eq …)`.
;
; FizzBuzz over [1..15]:
; if n % 15 == 0 print "FizzBuzz"
; else if n % 3 == 0 print "Fizz"
; else if n % 5 == 0 print "Buzz"
; else print (show n)
;
; Equality dispatches through prelude.Eq.eq at Int (intercept arm
; eq__Int → `icmp eq i64`). Demonstrates the LLM-natural shape of
; `(app eq …)` on the primitive type that dominates most numerical
; code.
;
; Expected stdout (one per line):
; 1
; 2
; Fizz
; 4
; Buzz
; Fizz
; 7
; 8
; Fizz
; Buzz
; 11
; Fizz
; 13
; 14
; FizzBuzz
(module eqord_1_fizzbuzz
(fn classify
(doc "Return the FizzBuzz label for n, or the empty string if n is a plain number.")
(type
(fn-type
(params (con Int))
(ret (con Str))))
(params n)
(body
(if (app eq (app % n 15) 0)
"FizzBuzz"
(if (app eq (app % n 3) 0)
"Fizz"
(if (app eq (app % n 5) 0)
"Buzz"
"")))))
(fn emit_one
(doc "Print the FizzBuzz label or the number itself.")
(type (fn-type (params (con Int)) (ret (con Unit)) (effects IO)))
(params n)
(body
(let label (app classify n)
(if (app eq label "")
(app print n)
(app print label)))))
(fn loop
(doc "Tail-recursive driver over [i..stop].")
(type (fn-type (params (con Int) (con Int)) (ret (con Unit)) (effects IO)))
(params i stop)
(body
(if (app gt i stop)
(lit-unit)
(seq
(app emit_one i)
(tail-app loop (app + i 1) stop)))))
(fn main
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body (app loop 1 15))))