# Generics and constraints

> Write reusable typed classes and methods, use constraints, and consume generic .NET APIs.

Canonical: https://alice-lang.org/documentation/language/generics/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

## Generic classes

A type parameter describes a family of types without giving up static checking. Alice uses parentheses for type arguments.

```alice
class Box(T)
  getter item : T
  def initialize(@item : T)
  end
end
puts Box(String).new("curious").item.upcase
puts Box(Int32).new(42).item
```

Expected output:

```text
CURIOUS
42
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.Y2xhc3MgQm94KFQpCiAgZ2V0dGVyIGl0ZW0gOiBUCiAgZGVmIGluaXRpYWxpemUoQGl0ZW0gOiBUKQogIGVuZAplbmQKcHV0cyBCb3goU3RyaW5nKS5uZXcoImN1cmlvdXMiKS5pdGVtLnVwY2FzZQpwdXRzIEJveChJbnQzMikubmV3KDQyKS5pdGVt)

`Box(String)` and `Box(Int32)` are different constructed CLR types. The `item` getter returns the corresponding type, so a string-specific operation remains statically checked.

The same notation applies to .NET types: `List(String)`, `Dictionary(String, Int32)`, and `IComparable(T)`.

## Generic methods

```alice
def biggest(a : T, b : T) : T forall T where T : IComparable(T)
  a.CompareTo(b) >= 0 ? a : b
end
puts biggest(3, 7)
puts biggest("a", "b")
```

`forall T` introduces the type parameter. `where T : IComparable(T)` states that values of `T` support comparison. The call-site arguments can infer `T`; incompatible arguments or missing constraints cause compilation errors.

Constraints also apply to generic class declarations, such as `class Node(T) where T : IComparable(T)`.

## Generics versus untyped parameters

An untyped Alice parameter can specialize for each call-site type. A generic declaration states a reusable relationship between types: for example both arguments and the result are the same `T`. Use a constraint to describe the members the implementation needs.

`Any` is different again: it opts into runtime member binding. It is useful for dynamic boundaries, but it removes useful static information from a normally typed API.

## .NET method inference

.NET generic methods can often infer their type arguments from arguments and typed lambdas. If inference fails (AL0312), make the argument types explicit or use the target API's generic arguments. Check the library's declared overloads when a call is ambiguous.

```alice
xs = [3, 1, 2]
puts xs.OrderBy(->(x : Int32) { x }).First()
```

Use the actual .NET member name and delegate signature. Install NuGet dependencies and restore them before expecting the compiler or editor to resolve external generic APIs.

## Continue reading

- Previous: [Modules and mixins](https://alice-lang.org/documentation/language/modules.md)
- Next: [Enums and pattern matching](https://alice-lang.org/documentation/language/pattern-matching.md)
