Files
AILang/design/contracts/0017-prelude-classes.md
T
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
All 176 files in the four accumulating directories now use a
zero-padded 4-digit counter prefix that reflects creation order
(`NNNN-slug.md`). The counter is assigned per directory in strict
git-log creation order; ties broken alphabetically by original name.
The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is
dropped — the date is recoverable from git log and the counter
carries the ordering.

A file's counter is stable for the life of the file: never reassigned,
never reused, never compacted. Deleted files retire their counter;
subsequent files do not fill the gap. This is the property that lets
cross-references stay literal — refs use the full filename including
the counter (`design/contracts/0007-honesty-rule.md`) so they grep
cleanly and resolve directly without a glob step.

313 cross-references updated across .md/.rs/.toml/.c/.json files
(test pins, include_str! paths, design-INDEX entries, baseline notes,
runtime C comments, inter-contract markdown links incl. bare basename
and `../models/foo.md` forms).

CLAUDE.md gets a new "File-naming convention" section spelling out
the rule and rationale. skills/brainstorm/SKILL.md and
skills/planner/SKILL.md updated so new spec/plan creation produces
counter-prefixed names from the start.

The full test suite (cargo test --workspace) passes.
2026-05-28 13:31:31 +02:00

4.0 KiB

Prelude (built-in) classes

Prelude (built-in) classes

The prelude ships the Ordering ADT, the Eq and Ord classes, primitive Eq Int/Bool/Str/Unit and Ord Int/Bool/Str instances, and the five polymorphic free-fn helpers ne/lt/le/gt/ge. The primitive Eq / Ord instance bodies are placeholder lambdas in examples/prelude.ail; the codegen intercept try_emit_primitive_instance_body overrides them with single-instruction bodies (icmp eq i64 for eq__Int, icmp eq i1 for eq__Bool, @ail_str_eq for eq__Str, ret i1 1 for eq__Unit; a three-way icmp ladder constructing LT/EQ/GT for compare__T) and attaches alwaysinline so the call folds to the single instruction at every use site.

The five Ord-class free helpers (ne/lt/le/gt/ge) have source-level bodies that route through eq / compare plus match over Ordering — written that way so the surface form is LLM-natural and so user-ADT instances of Eq/Ord automatically pick up working helpers without any per-type opt-in. At Int the codegen intercept short-circuits this indirection: lt__Int, le__Int, gt__Int, ge__Int, and ne__Int are intercept- overridden to emit icmp slt i64 / sle i64 / sgt i64 / sge i64 / ne i64 directly, bypassing the compare__IntOrdering-ctor → match path. Without this short-circuit the Ordering ctor allocates per call inside hot loops — a real cost surfaced by the bench_closure_chain regression at the operator-routing-eq-ord milestone and pinned by crates/ail/tests/ord_int_intercept_ir_pin.rs. The short-circuit is Int-only by deliberate asymmetry: no current bench or example fixture exercises Ord-lt/le/gt/ge at Bool or Str at sufficient call frequency to justify the intercept-arm maintenance; the symmetric extensions are mechanical when the first such workload appears.

Float has neither Eq nor Ord instance per Float semantics; a polymorphic helper invoked at Float fires NoInstance at typecheck with a Float-aware diagnostic that names the explicit float_eq / float_lt alternatives and cross-references this section. The lookup machinery that turns a show x call site into the right monomorphic instance is documented in method dispatch.

Float comparison is a separate surface: the six monomorphic prelude fns float_eq / float_ne / float_lt / float_le / float_gt / float_ge each carry the type (Float, Float) -> Bool without a class constraint. They lower via the same intercept machinery as the primitive Eq instances (single fcmp instruction with the matching predicate oeq / une / olt / ole / ogt / oge, plus alwaysinline). They replace what would otherwise be a polymorphic == / < on Float — Float gets full comparability via explicit named fns, not via an implicit class instance.

The prelude ships class Show a where show : (a borrow) -> Str and primitive Show Int, Show Bool, Show Str, Show Float instances. Float is included in Show (unlike Eq/Ord) — IEEE-754 makes structural equality and total ordering semantically dubious, but textual representation of a Float is well-defined modulo the NaN-spelling caveat in Float semantics. Each Show <T> instance body is a single-application lambda invoking the corresponding runtime primitive (int_to_str, bool_to_str, str_clone, float_to_str; see Str ABI for the heap-Str primitives); no codegen intercept is required. The polymorphic helper print : forall a. Show a => a -> () !IO has body \x -> let s = show x in do io/print_str s (explicit let-binder for heap-Str RC discipline per the Str carve-out in Str ABI). The let-binder is structurally pinned by crates/ail/tests/print_mono_body_shape.rs. Routing through print is the path for non-Str primitives; io/print_str is the only built-in direct-output effect-op.

Ratified by: crates/ail/tests/show_no_instance_e2e.rs.