Generics ​
Generics let you write types and functions that work with any type, providing flexibility without sacrificing type safety.
Generic Types ​
Generic Records ​
Create records that work with any type:
type Box[T] = record
value: T
end record
type Pair[A, B] = record
first: A
second: B
end recordUsing generic records:
-- Specify the type parameter when constructing (positional syntax)
int_box: Box[Int] = Box[Int](42)
str_box: Box[String] = Box[String]("hello")
pair: Pair[Int, String] = Pair[Int, String](1, "one")Generic Variants ​
Variants can also be generic:
type Maybe[T] = variant
Just(value: T)
Nothing
end variantThe built-in Option[T] and Result[T, E] are generic variants:
type Option[T] = variant
Some(value: T)
None
end variant
type Result[T, E] = variant
Ok(value: T)
Err(error: E)
end variantWorking with Generic Types ​
fn unwrap_or_default(box: Box[Int], default: Int) -> Int
box.value
end fn
-- Define a signature for the mapping function
type IntTransform = (Int) -> Int
fn map_maybe(m: Maybe[Int], f: IntTransform) -> Maybe[Int]
match m
Just(v) -> Just(f(v))
Nothing -> Nothing
end match
end fnConst Generics ​
Const generics allow compile-time integer parameters in types. This is useful for fixed-size data structures.
Defining Const Generic Types ​
Use N: Int syntax to declare a const parameter:
type Vector[N: Int] = record
data: List[Float]
end record
type Matrix[M: Int, N: Int] = record
data: List[Float]
end recordMixing Type and Const Parameters ​
You can combine regular type parameters with const parameters:
type SizedArray[T, N: Int] = record
elements: List[T]
capacity: Int
end recordUsing Const Generics ​
-- Integer literals in type positions (positional construction)
v3: Vector[3] = Vector[3]([1.0, 2.0, 3.0])
v4: Vector[4] = Vector[4]([1.0, 2.0, 3.0, 4.0])
m: Matrix[2, 3] = Matrix[2, 3]([1.0, 2.0, 3.0, 4.0, 5.0, 6.0])Type Safety with Const Generics ​
Different const values create different types:
v3: Vector[3] = Vector[3]([1.0, 2.0, 3.0])
v4: Vector[4] = Vector[4]([1.0, 2.0, 3.0, 4.0])
-- This would be a type error:
-- v3 = v4 -- ERROR: Vector[3] and Vector[4] are different typesThis catches size mismatches at compile time.
Generic Functions ​
Functions can have type parameters:
fn identity[T](x: T) -> T
x
end fn
fn flip_pair[A, B](pair: Tuple[A, B]) -> Tuple[B, A]
(a, b) = pair
(b, a)
end fn
fn main() -> Unit using file_io
-- Type inferred from context
n: Int = identity(42)
s: String = identity("hello")
-- Works with any types
flipped: Tuple[String, Int] = flip_pair((1, "one"))
print(to_string(n))
print(s)
end fnType parameters are inferred from usage context. You cannot explicitly specify type parameters at call sites (e.g., identity[Int](42) is not supported).
Note: swap is a reserved builtin for quantum SWAP gates.
See also Effect Polymorphism for functions that work with any effect.
Bounded Polymorphism ​
Type bounds constrain generic parameters to types that support specific operations:
fn double[T: Numeric](x: T) -> T
x + x
end fn
fn max[T: Comparable](a: T, b: T) -> T
match a > b, True -> a, False -> b
end fnAvailable Bounds ​
| Bound | Operations | Types |
|---|---|---|
Numeric | +, -, *, /, unary - | Int, Float, QInt |
Comparable | <, >, <=, >= | Int, Float |
HasLength | length() | String, List |
Linear | discard() | Qubit, QInt |
Note: Equality (==, !=) works on all primitive types without a bound.
Example ​
fn double[T: Numeric](x: T) -> T
x + x
end fn
fn main() -> Unit using file_io
print(to_string(double(5))) -- 10 (Int)
print(to_string(double(3.14))) -- 6.28 (Float)
end fnNext Steps ​
- Functional Programming — Higher-order functions and pipelines
- Effects System — Effect polymorphism for generic effectful code
- Linear Types — How generics interact with linear types