Generics
Almide uses [] for generics, not <>. This eliminates ambiguity with comparison operators and makes parsing unambiguous for both compilers and LLMs.
Generic functions
Section titled “Generic functions”Declare type parameters in [] after the function name:
fn identity[T](x: T) -> T = x
fn first[T](xs: List[T]) -> Option[T] = list.first(xs)
fn pair[A, B](a: A, b: B) -> (A, B) = (a, b)Type arguments are inferred at the call site:
let n = identity(42) // T = Intlet s = identity("hello") // T = Stringlet p = pair(1, "one") // A = Int, B = StringGeneric types
Section titled “Generic types”Types can be parameterized:
type Pair[A, B] = { first: A, second: B,}
type Tree[T] = | Leaf(T) | Node(Tree[T], Tree[T])Use with concrete types:
let p: Pair[Int, String] = { first: 1, second: "one" }let tree: Tree[Int] = Node(Leaf(1), Leaf(2))Built-in generic types
Section titled “Built-in generic types”The standard library uses generics extensively:
| Type | Description |
|---|---|
List[T] | Ordered homogeneous collection |
Map[K, V] | Key-value collection |
Set[T] | Unordered unique collection |
Option[T] | Optional value (some(x) or none) |
Result[T, E] | Success or failure (ok(x) or err(e)) |
Nested generics:
let data: Map[String, List[Int]] = ["scores": [90, 85, 92]]let result: Result[List[String], String] = ok(["a", "b"])Bounds
Section titled “Bounds”Type parameters can be constrained with protocol bounds using ::
fn show[T: Repr](item: T) -> String = item.repr()
fn process[T: Serializable](item: T) -> String = item.serialize()Multiple bounds are specified by listing protocols:
fn compare[T: Ord](a: T, b: T) -> Bool = a < bSee Protocols for defining custom protocols that can be used as bounds.
Type application syntax
Section titled “Type application syntax”Almide always uses [] for type arguments:
List[Int] // list of integersMap[String, List[Int]] // nested genericsResult[User, ParseError] // result typeFn(Int, Int) -> Bool // function type (no [] needed)<> is never used for generics. < and > are always comparison operators.
Why [] instead of <>
Section titled “Why [] instead of <>”Using [] for generics is a deliberate design choice:
<and>are always comparison operators, never part of type syntax- No ambiguity in expressions like
a < bvsf<T>(x) - Simpler parsing for both the compiler and LLMs generating code
- Common mistake prevention:
fn foo<T>(x: T)is rejected with a clear hint
Next steps
Section titled “Next steps”- Protocols — defining and implementing protocol bounds
- Types & Values — records, variants, and type aliases
- Functions — generic functions and type inference