Files
AILang/examples/std_list.ail.json
T
Brummel 92f4b4f8c7 Iter 15b: std_list ships, three more compiler gaps closed
Second stdlib module. Tester wrote std_list.ailx (10 combinators,
164 LOC) and a consumer demo. std_list typechecked standalone;
demo did not, surfacing three compiler bugs:

1. Check-side: Iter 14h's qualify_local_types was applied to
   Term::Var cross-module lookup but not to ctor-field types in
   Term::Ctor synth or Pattern::Ctor resolution. First recursive
   cross-module ADT (List has Cons a (List a) — recursive Con
   self-ref) triggers the bug. std_maybe slipped through because
   Maybe's ctors have no recursive Con field.
2. Codegen-side: same gap mirrored across 4 sites in codegen
   (Term::Ctor synth, lower_ctor, lower_match) plus a tweak to
   unify_for_subst (recurse on re-bind instead of strict equality
   so sibling-derived List<Int> accepts nullary-ctor's List<$u>
   wildcard).
3. Const codegen: emit_const rejected non-literal const bodies.
   The demo's xs : List<Int> = Cons 1 (...) requires it. Fix:
   per-module const table, Term::Var resolution loads literal
   consts from global, inlines non-literal bodies. Bare and
   qualified refs both supported.

All three fixes carry an "Iter 15b" code comment at their site.
~349/25 LOC across ailang-check, ailang-codegen, e2e.rs.

Tests 85 -> 87. New e2e std_list_demo asserts 11-line stdout:
length 5, is_empty false/true, head via from_maybe, tail length,
append length, reverse head, map double head, filter is_even
length, fold_left sum, fold_right sum. New ailang-check unit
test cross_module_recursive_adt_term_and_pat_ctor covers both
the original bug and the symmetric pat-ctor latent twin.

Hash invariance: all pre-15b fixtures + std_maybe defs
bit-identical. 14a / 14e / 14h regressions all green.

Cumulative state: 2 stdlib modules (std_maybe, std_list), 14
combinators, cross-module recursive ADT working end-to-end.
Three compiler bugs surfaced + fixed in dogfood since 14a (each
dogfood iter has surfaced ≥1).

Authoring observation: form (A) at 10 combinators is fine; main
friction is paren-counting in nested seq chains, not the form
itself. n-ary seq would help but is sugar.

Plan 15c: 1000-element list stress test for fold_left (tail-call-
marked) vs fold_right (constructor-blocked).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:34:49 +02:00

1 line
8.2 KiB
JSON

{"defs":[{"ctors":[{"fields":[],"name":"Nil"},{"fields":[{"k":"var","name":"a"},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"name":"Cons"}],"doc":"Polymorphic singly-linked list: Nil or Cons<a, List<a>>.","kind":"type","name":"List","vars":["a"]},{"body":{"arms":[{"body":{"name":"acc","t":"var"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"name":"f","t":"var"},{"args":[{"name":"acc","t":"var"},{"name":"h","t":"var"}],"fn":{"name":"f","t":"var"},"t":"app"},{"name":"t","t":"var"}],"fn":{"name":"fold_left","t":"var"},"t":"app","tail":true},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Tail-recursive left fold. The recursive call is in tail position and is marked.","kind":"fn","name":"fold_left","params":["f","acc","xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"effects":[],"k":"fn","params":[{"k":"var","name":"b"},{"k":"var","name":"a"}],"ret":{"k":"var","name":"b"}},{"k":"var","name":"b"},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"k":"var","name":"b"}},"k":"forall","vars":["a","b"]}},{"body":{"arms":[{"body":{"name":"acc","t":"var"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"name":"h","t":"var"},{"args":[{"name":"f","t":"var"},{"name":"acc","t":"var"},{"name":"t","t":"var"}],"fn":{"name":"fold_right","t":"var"},"t":"app"}],"fn":{"name":"f","t":"var"},"t":"app"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Right fold. Constructor-blocked: the recursive call is the second arg of f, NOT a tail position.","kind":"fn","name":"fold_right","params":["f","acc","xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"effects":[],"k":"fn","params":[{"k":"var","name":"a"},{"k":"var","name":"b"}],"ret":{"k":"var","name":"b"}},{"k":"var","name":"b"},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"k":"var","name":"b"}},"k":"forall","vars":["a","b"]}},{"body":{"args":[{"body":{"args":[{"name":"c","t":"var"},{"lit":{"kind":"int","value":1},"t":"lit"}],"fn":{"name":"+","t":"var"},"t":"app"},"effects":[],"paramTypes":[{"k":"con","name":"Int"},{"k":"var","name":"a"}],"params":["c","_"],"retType":{"k":"con","name":"Int"},"t":"lam"},{"lit":{"kind":"int","value":0},"t":"lit"},{"name":"xs","t":"var"}],"fn":{"name":"fold_left","t":"var"},"t":"app"},"doc":"Length via fold_left. The accumulating lambda ignores the element and increments the counter.","kind":"fn","name":"length","params":["xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"k":"con","name":"Int"}},"k":"forall","vars":["a"]}},{"body":{"args":[{"body":{"args":[{"name":"h","t":"var"},{"name":"acc","t":"var"}],"ctor":"Cons","t":"ctor","type":"List"},"effects":[],"paramTypes":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"},{"k":"var","name":"a"}],"params":["acc","h"],"retType":{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"},"t":"lam"},{"args":[],"ctor":"Nil","t":"ctor","type":"List"},{"name":"xs","t":"var"}],"fn":{"name":"fold_left","t":"var"},"t":"app"},"doc":"Reverse via fold_left with a flipping accumulator. Tail-recursive by virtue of the fold_left call.","kind":"fn","name":"reverse","params":["xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}},"k":"forall","vars":["a"]}},{"body":{"arms":[{"body":{"lit":{"kind":"bool","value":true},"t":"lit"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"lit":{"kind":"bool","value":false},"t":"lit"},"pat":{"ctor":"Cons","fields":[{"p":"wild"},{"p":"wild"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"True iff the list is Nil.","kind":"fn","name":"is_empty","params":["xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"k":"con","name":"Bool"}},"k":"forall","vars":["a"]}},{"body":{"arms":[{"body":{"args":[],"ctor":"Nothing","t":"ctor","type":"std_maybe.Maybe"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"name":"h","t":"var"}],"ctor":"Just","t":"ctor","type":"std_maybe.Maybe"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"p":"wild"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Returns Just<a> of the first element, or Nothing for an empty list. Cross-module Maybe.","kind":"fn","name":"head","params":["xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"k":"var","name":"a"}],"k":"con","name":"std_maybe.Maybe"}},"k":"forall","vars":["a"]}},{"body":{"arms":[{"body":{"args":[],"ctor":"Nothing","t":"ctor","type":"std_maybe.Maybe"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"name":"t","t":"var"}],"ctor":"Just","t":"ctor","type":"std_maybe.Maybe"},"pat":{"ctor":"Cons","fields":[{"p":"wild"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Returns Just<List<a>> of the tail, or Nothing for an empty list.","kind":"fn","name":"tail","params":["xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"k":"con","name":"std_maybe.Maybe"}},"k":"forall","vars":["a"]}},{"body":{"arms":[{"body":{"name":"ys","t":"var"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"name":"h","t":"var"},{"args":[{"name":"t","t":"var"},{"name":"ys","t":"var"}],"fn":{"name":"append","t":"var"},"t":"app"}],"ctor":"Cons","t":"ctor","type":"List"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Concatenate two lists. Constructor-blocked recursion: the recursive call is inside Cons, NOT a tail.","kind":"fn","name":"append","params":["xs","ys"],"type":{"body":{"effects":[],"k":"fn","params":[{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}},"k":"forall","vars":["a"]}},{"body":{"arms":[{"body":{"args":[],"ctor":"Nil","t":"ctor","type":"List"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"args":[{"args":[{"name":"h","t":"var"}],"fn":{"name":"f","t":"var"},"t":"app"},{"args":[{"name":"f","t":"var"},{"name":"t","t":"var"}],"fn":{"name":"map","t":"var"},"t":"app"}],"ctor":"Cons","t":"ctor","type":"List"},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Polymorphic map. Constructor-blocked recursion.","kind":"fn","name":"map","params":["f","xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"effects":[],"k":"fn","params":[{"k":"var","name":"a"}],"ret":{"k":"var","name":"b"}},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"k":"var","name":"b"}],"k":"con","name":"List"}},"k":"forall","vars":["a","b"]}},{"body":{"arms":[{"body":{"args":[],"ctor":"Nil","t":"ctor","type":"List"},"pat":{"ctor":"Nil","fields":[],"p":"ctor"}},{"body":{"cond":{"args":[{"name":"h","t":"var"}],"fn":{"name":"p","t":"var"},"t":"app"},"else":{"args":[{"name":"p","t":"var"},{"name":"t","t":"var"}],"fn":{"name":"filter","t":"var"},"t":"app"},"t":"if","then":{"args":[{"name":"h","t":"var"},{"args":[{"name":"p","t":"var"},{"name":"t","t":"var"}],"fn":{"name":"filter","t":"var"},"t":"app"}],"ctor":"Cons","t":"ctor","type":"List"}},"pat":{"ctor":"Cons","fields":[{"name":"h","p":"var"},{"name":"t","p":"var"}],"p":"ctor"}}],"scrutinee":{"name":"xs","t":"var"},"t":"match"},"doc":"Keep elements where p is true. Constructor-blocked when p holds; non-tail recursive call when p is false.","kind":"fn","name":"filter","params":["p","xs"],"type":{"body":{"effects":[],"k":"fn","params":[{"effects":[],"k":"fn","params":[{"k":"var","name":"a"}],"ret":{"k":"con","name":"Bool"}},{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}],"ret":{"args":[{"k":"var","name":"a"}],"k":"con","name":"List"}},"k":"forall","vars":["a"]}}],"imports":[{"module":"std_maybe"}],"name":"std_list","schema":"ailang/v0"}