feat(lang): L5 generic records and functions

property Pool<T> { ... }, function first<T>(xs: []T) -> T, map<T, U> over fn
types; a type writes an instance as Pool<Thing>, nested as deep as needed. The
parser names an instance Pool$Thing and remembers its generic and arguments; the
checker takes the generic declarations out, infers a call's type arguments from
its arguments or its result's declared slot, and makes each instance once as an
ordinary record or function, checked like any other. Errors print Pool<Thing>.
An instance keeps its generic's module and export (L3). ludic-fmt keeps type
arguments together while spacing comparisons and shifts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 02:21:09 +03:00
parent 57b66bdf47
commit 6a24b14f0e
26 changed files with 61796 additions and 51949 deletions

View file

@ -0,0 +1,8 @@
# L5: a generic takes as many type arguments as it has parameters
program GenericArity {
property Pool<T> { n: int = 0 }
entry {
let p = new Pool<int, string>
print(p.n)
}
}

View file

@ -0,0 +1,9 @@
# L5: two instances of one generic are two types
program GenericMismatch {
property Pool<T> { n: int = 0 }
entry {
let ints = new Pool<int>
let words: Pool<string> = ints
print(words.n)
}
}

View file

@ -0,0 +1,9 @@
# L5: a call whose type arguments nothing decides is refused - give the result a declared type
program GenericUnbound {
property Pool<T> { n: int = 0 }
function pool_new<T>() -> Pool<T> { return new Pool<T> }
entry {
let p = pool_new()
print(p.n)
}
}

View file

@ -0,0 +1,9 @@
# L5 + L3: an instance keeps its generic's module and export - a private generic stays private
import "private_kit"
program PrivateGeneric {
entry {
let p = new Pool<int>
grow(p)
print(pool_len(p))
}
}

View file

@ -0,0 +1,2 @@
module kit
import "pool.ludic"

View file

@ -0,0 +1,11 @@
export property Pool<T> {
items: []T = null
}
function grow<T>(p: Pool<T>) -> void {
if p.items == null { p.items = new []T }
}
export function pool_add<T>(p: Pool<T>, x: T) -> void {
grow(p)
push(p.items, x)
}
export function pool_len<T>(p: Pool<T>) -> int { return len(p.items) }