Files
AILang/examples/fieldtest/eqord_2_rational_eq.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

57 lines
2.2 KiB
Plaintext

; Fieldtest fixture — Axis 2: user-ADT Eq instance, hand-written.
;
; Rational numbers compared by cross-multiplication:
; a/b == c/d iff a*d == b*c
; (assumes positive denominators; the constructor enforces it.)
;
; The interesting bit for the milestone is the *nested* equality call
; inside the user instance body: the inner `(app eq (app * a d) (app * b c))`
; dispatches to prelude.eq__Int (the primitive instance, intercept-
; lowered to `icmp eq i64`), while the outer `(app eq r1 r2)` from main
; dispatches to this module's own `eq__Rational`. End-to-end the chain
; is: user-instance method → prelude class method → primitive intercept.
;
; Expected stdout (one per line):
; true ; 1/2 == 2/4
; false ; 1/2 == 1/3
; true ; 3/4 == 6/8
; false ; 5/6 == 4/5
(module eqord_2_rational_eq
(data Rational
(doc "Numerator + (positive) denominator. No normalisation.")
(ctor MkRational (con Int) (con Int)))
(instance
(class prelude.Eq)
(type (con Rational))
(doc "Cross-multiplication equality. Inner eq dispatches to prelude.eq__Int.")
(method eq
(body
(lam
(params (typed r1 (con Rational)) (typed r2 (con Rational)))
(ret (con Bool))
(body
(match r1
(case (pat-ctor MkRational a b)
(match r2
(case (pat-ctor MkRational c d)
(app eq (app * a d) (app * b c)))))))))))
(fn main
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body
(let r_1_2 (term-ctor Rational MkRational 1 2)
(let r_2_4 (term-ctor Rational MkRational 2 4)
(let r_1_3 (term-ctor Rational MkRational 1 3)
(let r_3_4 (term-ctor Rational MkRational 3 4)
(let r_6_8 (term-ctor Rational MkRational 6 8)
(let r_5_6 (term-ctor Rational MkRational 5 6)
(let r_4_5 (term-ctor Rational MkRational 4 5)
(seq (app print (app eq r_1_2 r_2_4))
(seq (app print (app eq r_1_2 r_1_3))
(seq (app print (app eq r_3_4 r_6_8))
(app print (app eq r_5_6 r_4_5)))))))))))))))