Generics
Write a function or container once for many types.
On this page
A generic function or class takes type parameters in [ ]. The compiler makes a separate version for each type it is used with and checks that version.
Generic functions#
fn repeat[T](value: T, times: uint) Array[T] {
let list = Array[T]{}
each 0..times as _ : list.append(value)
return list
}
fn join[A, B](a: A, b: B) String {
return a + "/" + b
}
fn main() {
let names = repeat[String]("Ada", 2)
println(names.length) // 2
println(join[int, String](1, "x")) // 1/x
}
Type parameter names are usually one capital letter, but any name works. Inside the body T is used like any type, including ?T or Array[T].
Infer the type from an argument#
Write $T in a parameter type to let the compiler take T from the argument. Later parameters and the result then use plain T:
fn largest(items: Array[$T]) ?T {
let best: ?T = null
each items as item {
if !isset(best) || item > best : best = item
}
return best
}
fn pair(a: $T, b: T) Array[T] {
return Array[T]{ a, b }
}
fn main() {
println(largest(Array[int]{ 3, 9, 4 }) ?? 0) // 9
println(pair("a", "b").length) // 2
}
$T also works inside other types: ?$T, &[$T], HashMap[$K, $V]. An &[$T] parameter takes arrays, slices and fixed arrays alike.
Generic classes#
class Stack[T] {
items: Array[T] (.{})
fn push(value: T) {
this.items.append(value)
}
fn pop() ?T {
return this.items.pop_last() !? null
}
}
fn main() {
let stack = Stack[String]{}
stack.push("a")
stack.push("b")
println(stack.pop() ?? "") // b
let numbers = Stack[int]{}
numbers.push(7)
println(numbers.items.length) // 1
}
Structs, traits, and modes take type parameters the same way.
Generic methods#
A method can have type parameters of its own. Built-in collections use this for functions like map:
fn main() {
let numbers = Array[int]{ 1, 2, 3 }
let labels = numbers.map[String](fn(n: int) String { return "#%n" })
println(labels.join(", ")) // #1, #2, #3
}
What the type must support#
The body may only use operations that every type it is used with supports. largest above uses >, so it works for numbers and strings, and for a class only when it defines an ordering operator. Using it with an unsupported type is a compile error at the call.
Type-specific code#
Compile-time conditions on T choose code per type. For example T.$is_nullable is true when T allows null:
fn describe[T](value: T) String {
#if T.$is_nullable
return "may be null"
#else
return "always set"
#end
}
fn main() {
println(describe[?String](null)) // may be null
println(describe[int](5)) // always set
}
See Compile-time code for other conditions.
Generic types are invariant#
Array[A] and Array[B] are different types even when an A converts to a B. Convert the elements instead of the container.

