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

62 lines
2.1 KiB
Plaintext

; Fieldtest fixture — Axis 3: Float comparison via the named-fn surface.
;
; Newton's method for sqrt: iterate x_{k+1} = (x_k + n/x_k) / 2 until
; |x_{k+1} - x_k| < tol
; Reaches for `float_lt` (loop convergence) and `float_eq` (zero-arg
; guard against pathological 0.0 input). Also exercises Float
; arithmetic + Show Float via `print`.
;
; The interesting bit for the milestone is that there is no
; polymorphic `eq` / `lt` at Float — those would fail at typecheck
; with `NoInstance Eq Float`. The LLM-author has to reach for the
; named family `float_eq` / `float_lt` / `float_gt`. The shape mirrors
; how Rust's `f64::abs` / partial_cmp guides users toward bespoke
; Float-aware methods.
;
; abs implemented inline as `if x < 0 then -x else x` using float_lt.
;
; Expected stdout (one per line):
; 2 (sqrt 4.0) ; 2.0
; ~3.16 (sqrt 10.0) ; 3.1622776601683795 — exact IEEE
; 0 (sqrt 0.0) ; 0.0, short-circuit
; 1 (sqrt 1.0) ; 1.0
(module eqord_3_newton_sqrt
(fn fabs
(doc "Float absolute value via the named-fn comparison surface.")
(type (fn-type (params (con Float)) (ret (con Float))))
(params x)
(body
(if (app float_lt x 0.0)
(app - 0.0 x)
x)))
(fn iterate
(doc "Tail-recursive Newton iteration. Stops when |xnew - x| < tol.")
(type (fn-type (params (con Float) (con Float) (con Float)) (ret (con Float))))
(params n x tol)
(body
(let xnew (app / (app + x (app / n x)) 2.0)
(if (app float_lt (app fabs (app - xnew x)) tol)
xnew
(tail-app iterate n xnew tol)))))
(fn sqrt
(doc "sqrt n via Newton with initial guess 1.0 and tolerance 1e-10. Returns 0.0 for n == 0.0; assumes n >= 0.")
(type (fn-type (params (con Float)) (ret (con Float))))
(params n)
(body
(if (app float_eq n 0.0)
0.0
(app iterate n 1.0 0.0000000001))))
(fn main
(type (fn-type (params) (ret (con Unit)) (effects IO)))
(params)
(body
(seq (app print (app sqrt 4.0))
(seq (app print (app sqrt 10.0))
(seq (app print (app sqrt 0.0))
(app print (app sqrt 1.0))))))))