generics (in the compiler written in Finch)

fn max[T](T a, T b) -> T      struct Stack[T] { []T items }      fn Stack[T].push(T x)

The type is worked out from the arguments (max(3, 4), first([1, 2])) and written out
where it cannot be: max[float](1, 2.5), Stack[int](), Pair[str, int](...). Each use makes
its own copy, so a generic costs nothing at run time. Several parameters
(Pair[A, B]), nesting (Stack[Pair[int, str]]), methods, and printing (Stack[int](items: ...))
work. An error inside a generic says which version it was building.

boot.sh: 37 passed, 0 failed (stage 2 and 3 identical).
This commit is contained in:
Nexoniarz 2026-10-25 20:53:13 +01:00
commit 04eb0cce55
10 changed files with 575 additions and 7 deletions

View file

@ -0,0 +1,15 @@
// expect: (while building bigger[Point])
struct Point {
int x
}
fn bigger[T](T a, T b) -> T {
if a > b {
return a
}
return b
}
fn main() {
print(bigger(Point(1), Point(2)))
}

View file

@ -0,0 +1,10 @@
// expect: can't tell what T is here
fn make[T]() -> []T {
[]T out
return out
}
fn main() {
x := make()
print(x)
}

View file

@ -0,0 +1,9 @@
// expect: 'Stack' is generic, so say which type it holds
struct Stack[T] {
[]T items
}
fn main() {
s := Stack()
print(s)
}

View file

@ -0,0 +1,8 @@
// expect: 'add' is not generic, so it takes no type arguments
fn add(int a, int b) -> int {
return a + b
}
fn main() {
print(add[int](1, 2))
}

View file

@ -0,0 +1,9 @@
// expect: Stack takes 1 type argument(s)
struct Stack[T] {
[]T items
}
fn main() {
Stack[int, str] s
print(s)
}

95
tests/new/generics.fch Normal file
View file

@ -0,0 +1,95 @@
struct Stack[T] {
[]T items
}
fn Stack[T].push(T x) {
self.items.push(x)
}
fn Stack[T].pop() -> T {
return self.items.pop()
}
fn Stack[T].size() -> int {
return self.items.len
}
struct Pair[A, B] {
A first
B second
}
fn Pair[A, B].swapped() -> Pair[B, A] {
return Pair[B, A](self.second, self.first)
}
struct Point {
int x
int y
}
fn max[T](T a, T b) -> T {
if a > b {
return a
}
return b
}
fn first[T]([]T items) -> T {
return items[0]
}
fn count[T]([]T items, T wanted) -> int {
n := 0
for x in items {
if x == wanted {
n += 1
}
}
return n
}
fn show[T](T x) -> str {
return "<" + str(x) + ">"
}
fn convert[T](str s) -> T {
return T(s)
}
fn fill[T](int n, T value) -> []T {
[]T out
for i in 0..n {
out.push(value)
}
return out
}
fn main() {
print(max(3, 9), max(2.5, 1.5), max("apple", "pear"), max('a', 'b'))
print(max[float](1, 2.5))
print(first(["x", "y"]), first([7, 8]), count([1, 2, 1, 3, 1], 1), count(["a", "b"], "b"))
print(show(5), show("hi"), show(Point(1, 2)), show([1, 2]))
print(fill(3, "ab"), fill(2, 1.5), fill[int](2, 7))
Stack[int] s
s.push(10)
s.push(20)
print(s.pop(), s.size(), s)
names := Stack[str]()
names.push("ann")
names.push("bob")
print(names, names.size())
pts := Stack[Point]()
pts.push(Point(1, 2))
print(pts.pop().x)
p := Pair[str, int]("age", 31)
q := p.swapped()
print(p, q, q.first + 1)
nested := Stack[Pair[int, str]]()
nested.push(Pair[int, str](1, "one"))
print(nested)
}

10
tests/new/generics.out Normal file
View file

@ -0,0 +1,10 @@
9 2.5 pear b
2.5
x 7 3 1
<5> <hi> <Point(x: 1, y: 2)> <[1, 2]>
["ab", "ab", "ab"] [1.5, 1.5] [7, 7]
20 1 Stack[int](items: [10])
Stack[str](items: ["ann", "bob"]) 2
1
Pair[str, int](first: "age", second: 31) Pair[int, str](first: 31, second: "age") 32
Stack[Pair[int, str]](items: [Pair[int, str](first: 1, second: "one")])