Generic Functions
Generic functions allow you to write type-flexible code that works with multiple types.
Syntax
Use angle brackets <T> to define type parameters:
fn identity<T>(x:T -- y:T) {
-> val
val
}
Basic usage
The compiler automatically infers types based on the arguments:
fn identity<T>(x:T -- y:T) {
-> val
val
}
fn main() {
42 identity print nl // Works with i64
3.14 identity print nl // Works with f64
"hello" identity print nl // Works with str
}
Multiple type parameters
Functions can have multiple type parameters:
fn pair<A B>(a:A b:B -- a:A b:B) {
-> second
-> first
first second
}
fn main() {
1 "one" pair
-> s
-> n
n print " " print s print nl // 1 one
}
Common patterns
Swap
fn swap<T>(a:T b:T -- b:T a:T) {
-> second
-> first
second first
}
fn main() {
10 20 swap
-> a -> b
a print " " print b print nl // 20 10
}
Apply
fn apply<T>(x:T f:fn(T -- T) -- result:T) {
-> f -> x
x f call
}
fn main() {
5 fn (n:i64 -- r:i64) { 2 * } apply print nl // 10
}
Generic arrays
The make<T> builtin creates typed arrays:
fn main() {
5 make<i64> -> ints // Integer array
5 make<f64> -> floats // Float array
5 make<str> -> strings // String array
5 make<ptr> -> ptrs // Pointer array
}
Type inference
The compiler infers types at the call site. Each call can use different types:
fn identity<T>(x:T -- y:T) { -> v v }
fn main() {
42 identity // T = i64
3.14 identity // T = f64
"hi" identity // T = str
}
Limitations
- Type parameters must be concrete types (
i64,f64,str,ptr) - No type constraints (all types are accepted)
- No generic structs (only generic functions)
Implementation
Generics use monomorphization: the compiler generates specialized versions for each type combination used. This provides the same performance as hand-written specialized functions.