# Prelude (built-in) classes ## Prelude (built-in) classes The prelude ships the `Ordering` ADT, the `Eq` and `Ord` [classes](0013-typeclasses.md), 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 carry the `(intrinsic)` marker in `examples/prelude.ail` (the marker is the lockstep partner to the `INTERCEPTS` registry); the codegen intercept `try_emit_primitive_instance_body` supplies their 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__Int` → `Ordering`-ctor → `match` path. Without this short-circuit the `Ordering` ctor allocates per call inside hot loops; the short-circuit is pinned by `crates/ail/tests/ord_int_intercept_ir_pin.rs`. It 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. Float has neither `Eq` nor `Ord` instance per [Float semantics]( 0005-float-semantics.md); 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](0016-method-dispatch.md). 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](0005-float-semantics.md). Each `Show ` 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](0011-str-abi.md) 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](0011-str-abi.md)). 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`.