Skip to content

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:

kettle
type Box[T] = record
  value: T
end record

type Pair[A, B] = record
  first: A
  second: B
end record

Using generic records:

kettle
-- Specify the type parameter when constructing
int_box: Box[Int] = Box[Int](value = 42)
str_box: Box[String] = Box[String](value = "hello")

pair: Pair[Int, String] = Pair[Int, String](first = 1, second = "one")

Generic Variants ​

Variants can also be generic:

kettle
type Maybe[T] = variant
  Just(value: T)
  Nothing
end variant

The built-in Option[T] and Result[T, E] are generic variants:

kettle
type Option[T] = variant
  Some(value: T)
  None
end variant

type Result[T, E] = variant
  Ok(value: T)
  Err(error: E)
end variant

Working with Generic Types ​

kettle
fn unwrap_or_default(box: Box[Int], default: Int) -> Int
  box.value
end fn

fn map_maybe(m: Maybe[Int], f: Fn(Int) -> Int) -> Maybe[Int]
  match m
    Just(v) -> Just(f(v))
    Nothing -> Nothing
  end match
end fn

Const 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:

kettle
type Vector[N: Int] = record
  data: List[Float]
end record

type Matrix[M: Int, N: Int] = record
  rows: Int
  cols: Int
  data: List[Float]
end record

Mixing Type and Const Parameters ​

You can combine regular type parameters with const parameters:

kettle
type SizedArray[T, N: Int] = record
  elements: List[T]
  capacity: Int
end record

Using Const Generics ​

kettle
-- Integer literals in type positions
v3: Vector[3] = Vector[3](data = [1.0, 2.0, 3.0])
v4: Vector[4] = Vector[4](data = [1.0, 2.0, 3.0, 4.0])

m: Matrix[2, 3] = Matrix[2, 3](rows = 2, cols = 3, data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0])

Type Safety with Const Generics ​

Different const values create different types:

kettle
v3: Vector[3] = Vector[3](data = [1.0, 2.0, 3.0])
v4: Vector[4] = Vector[4](data = [1.0, 2.0, 3.0, 4.0])

-- This would be a type error:
-- v3 = v4  -- ERROR: Vector[3] and Vector[4] are different types

This catches size mismatches at compile time.

Generic Functions ​

Functions can have type parameters:

kettle
fn identity[T](x: T) -> T
  x
end fn

fn swap[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
  swapped: Tuple[String, Int] = swap((1, "one"))
  print(to_string(n))
  print(s)
end fn

Type parameters are inferred from usage context. You cannot explicitly specify type parameters at call sites (e.g., identity[Int](42) is not supported).

See also Effect Polymorphism for functions that work with any effect.

Next Steps ​