747b7cd05c
Dogfood payoff for parameterised ADTs (13a/b/c). Adds the first
program to nest a nullary ctor of a parameterised ADT inside a
parent ctor (Cons(Int, Nil)) and to call a polymorphic recursive
higher-order fn over a recursive parameterised ADT.
Fixture (examples/list_map_poly.ail.json):
- data List a = Nil | Cons a (List a)
- inc : (Int) -> Int = \x. x + 1
- map : forall a b. ((a) -> b, List<a>) -> List<b>
recursive, instantiated at (Int, Int)
- print_list : (List<Int>) -> Unit !{IO}, recursive
- main builds [1,2,3], maps inc, prints each: "2", "3", "4"
E2E test list_map_poly_inc_then_prints in crates/ail/tests/e2e.rs.
Bug fixed (crates/ailang-codegen/src/lib.rs, +30/-9):
synth_arg_type used Type::unit() as placeholder for ADT type
vars that the ctor's args couldn't pin (Nil for List<a>).
Inside Cons(Int, Nil), unify_for_subst then bound a=Int from
the head and collided with a=Unit from the tail. Replaced the
placeholder with a synth-only wildcard Type::Var{name:"$u"}
mirroring the checker's $m metavar convention; unify_for_subst
short-circuits on $u-prefixed arg-side vars (accept without
binding, let a sibling pin the var).
No schema or API change. No new variant. Tester's recursion
hypothesis was refuted by debugger via a non-recursive
Cons(7, Nil) repro before the fix landed.
Tests: 25/25 e2e (was 24). All Iter 12/13 regressions green.
cargo doc --no-deps zero warnings (workspace invariant from
13d/e/f preserved).
Three-agent flow (tester -> debugger, no implementer needed
since the fix was inside debugger's <50 LOC scope). Process
note in JOURNAL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
231 lines
5.9 KiB
JSON
231 lines
5.9 KiB
JSON
{
|
|
"schema": "ailang/v0",
|
|
"name": "list_map_poly",
|
|
"imports": [],
|
|
"defs": [
|
|
{
|
|
"kind": "type",
|
|
"name": "List",
|
|
"vars": ["a"],
|
|
"doc": "Polymorphic singly-linked list.",
|
|
"ctors": [
|
|
{ "name": "Nil", "fields": [] },
|
|
{
|
|
"name": "Cons",
|
|
"fields": [
|
|
{ "k": "var", "name": "a" },
|
|
{
|
|
"k": "con",
|
|
"name": "List",
|
|
"args": [{ "k": "var", "name": "a" }]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"kind": "fn",
|
|
"name": "inc",
|
|
"type": {
|
|
"k": "fn",
|
|
"params": [{ "k": "con", "name": "Int" }],
|
|
"ret": { "k": "con", "name": "Int" },
|
|
"effects": []
|
|
},
|
|
"params": ["x"],
|
|
"doc": "Add 1 to an Int.",
|
|
"body": {
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "+" },
|
|
"args": [
|
|
{ "t": "var", "name": "x" },
|
|
{ "t": "lit", "lit": { "kind": "int", "value": 1 } }
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"kind": "fn",
|
|
"name": "map",
|
|
"type": {
|
|
"k": "forall",
|
|
"vars": ["a", "b"],
|
|
"body": {
|
|
"k": "fn",
|
|
"params": [
|
|
{
|
|
"k": "fn",
|
|
"params": [{ "k": "var", "name": "a" }],
|
|
"ret": { "k": "var", "name": "b" },
|
|
"effects": []
|
|
},
|
|
{
|
|
"k": "con",
|
|
"name": "List",
|
|
"args": [{ "k": "var", "name": "a" }]
|
|
}
|
|
],
|
|
"ret": {
|
|
"k": "con",
|
|
"name": "List",
|
|
"args": [{ "k": "var", "name": "b" }]
|
|
},
|
|
"effects": []
|
|
}
|
|
},
|
|
"params": ["f", "xs"],
|
|
"doc": "Polymorphic map: apply f to every element, recursing on the tail.",
|
|
"body": {
|
|
"t": "match",
|
|
"scrutinee": { "t": "var", "name": "xs" },
|
|
"arms": [
|
|
{
|
|
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
|
|
"body": {
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Nil",
|
|
"args": []
|
|
}
|
|
},
|
|
{
|
|
"pat": {
|
|
"p": "ctor",
|
|
"ctor": "Cons",
|
|
"fields": [
|
|
{ "p": "var", "name": "h" },
|
|
{ "p": "var", "name": "t" }
|
|
]
|
|
},
|
|
"body": {
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Cons",
|
|
"args": [
|
|
{
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "f" },
|
|
"args": [{ "t": "var", "name": "h" }]
|
|
},
|
|
{
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "map" },
|
|
"args": [
|
|
{ "t": "var", "name": "f" },
|
|
{ "t": "var", "name": "t" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"kind": "fn",
|
|
"name": "print_list",
|
|
"type": {
|
|
"k": "fn",
|
|
"params": [
|
|
{
|
|
"k": "con",
|
|
"name": "List",
|
|
"args": [{ "k": "con", "name": "Int" }]
|
|
}
|
|
],
|
|
"ret": { "k": "con", "name": "Unit" },
|
|
"effects": ["IO"]
|
|
},
|
|
"params": ["xs"],
|
|
"doc": "Print each Int on its own line.",
|
|
"body": {
|
|
"t": "match",
|
|
"scrutinee": { "t": "var", "name": "xs" },
|
|
"arms": [
|
|
{
|
|
"pat": { "p": "ctor", "ctor": "Nil", "fields": [] },
|
|
"body": { "t": "lit", "lit": { "kind": "unit" } }
|
|
},
|
|
{
|
|
"pat": {
|
|
"p": "ctor",
|
|
"ctor": "Cons",
|
|
"fields": [
|
|
{ "p": "var", "name": "h" },
|
|
{ "p": "var", "name": "t" }
|
|
]
|
|
},
|
|
"body": {
|
|
"t": "seq",
|
|
"lhs": {
|
|
"t": "do",
|
|
"op": "io/print_int",
|
|
"args": [{ "t": "var", "name": "h" }]
|
|
},
|
|
"rhs": {
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "print_list" },
|
|
"args": [{ "t": "var", "name": "t" }]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
"kind": "fn",
|
|
"name": "main",
|
|
"type": {
|
|
"k": "fn",
|
|
"params": [],
|
|
"ret": { "k": "con", "name": "Unit" },
|
|
"effects": ["IO"]
|
|
},
|
|
"params": [],
|
|
"doc": "Map inc over [1,2,3] via polymorphic List, then print: 2,3,4.",
|
|
"body": {
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "print_list" },
|
|
"args": [
|
|
{
|
|
"t": "app",
|
|
"fn": { "t": "var", "name": "map" },
|
|
"args": [
|
|
{ "t": "var", "name": "inc" },
|
|
{
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Cons",
|
|
"args": [
|
|
{ "t": "lit", "lit": { "kind": "int", "value": 1 } },
|
|
{
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Cons",
|
|
"args": [
|
|
{ "t": "lit", "lit": { "kind": "int", "value": 2 } },
|
|
{
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Cons",
|
|
"args": [
|
|
{ "t": "lit", "lit": { "kind": "int", "value": 3 } },
|
|
{
|
|
"t": "ctor",
|
|
"type": "List",
|
|
"ctor": "Nil",
|
|
"args": []
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|