
Generics let you write code that works with any type. Instead of writing separate functions for a list of integers and a list of strings, you write it once using a placeholder like `T`. The compiler fills in the actual type each time you use it, so you get type safety without repeating yourself.

Go shipped without generics for over a decade, and some developers preferred it that way. The tradeoff is real: deeply nested types like `Dict[Str, List[Pair[Int, Int]]]` are hard to read. But without generics you end up duplicating code or losing type safety by falling back to `Any`. Vary includes generics but keeps them simple: optional bounds via interface constraints, no variance annotations, and types are inferred at call sites so you rarely write them out.

## Design choices

| Choice | Decision | Rationale |
|--------|----------|-----------|
| Syntax | Square brackets `[T]` | Angle brackets `<T>` conflict with comparison operators in the parser. Square brackets are unambiguous and match Python's type hint syntax. |
| Inference | Always inferred at call sites | You never write type arguments explicitly. The compiler infers them from the values you pass. This keeps call sites clean. |
| Erasure | Type erasure on the JVM | Same approach as Java. Type parameters are checked at compile time and erased in bytecode. No runtime overhead, no reified types. |
| Bounds | Optional interface bounds | Type parameters can declare an upper bound: `[T: Comparable]`. The compiler enforces the constraint at every call site and allows calling bound interface methods on the type parameter. Unbounded parameters are still supported. |
| Variance | Invariant only | No covariance or contravariance annotations. `List[Cat]` is not a subtype of `List[Animal]`. This avoids the complexity of variance rules and the unsoundness problems that come with them. |

## Generic classes

Type parameters go in square brackets after the class name:

```vary
class Box[T](value: T) {
    let value: T = value

    def get(self) -> T {
        return self.value
    }
}

let int_box = Box(42)
let str_box = Box("hello")
print(int_box.get())        # 42
print(str_box.get())        # hello
```

Type arguments are inferred from constructor arguments. You write `Box(42)`, not `Box[Int](42)`.

Multiple type parameters are comma-separated:

```vary
class Pair[A, B](first: A, second: B) {
    let first: A = first
    let second: B = second
}

let p = Pair(1, "one")
print(p.first)              # 1
print(p.second)             # one
```

## Generic data types

Data types work the same way:

```vary
data Entry[K, V] {
    key: K
    value: V
}

let e = Entry("name", 42)
print(e)                    # Entry(key=name, value=42)
```

## Generic functions

Functions declare type parameters after the function name:

```vary
def identity[T](x: T) -> T {
    return x
}

def first[T](items: List[T]) -> T {
    return items[0]
}

def swap[A, B](pair: (A, B)) -> (B, A) {
    let (a, b) = pair
    return (b, a)
}

print(identity(42))         # 42
print(identity("hello"))    # hello
print(swap((1, "x")))       # (x, 1)
```

The type parameter is inferred from the argument, so you call `identity(42)` without specifying the type.

## Bounded type parameters

A type parameter can declare an upper bound using `: InterfaceName`. The compiler ensures every type argument satisfies the bound, and lets you call bound interface methods on the type parameter:

```vary
interface Printable {
    def display(self) -> Str {
    }
}

def show_all[T: Printable](items: List[T]) -> None {
    for item in items {
        print(item.display())
    }
}
```

Without the bound, calling `item.display()` would be a type error because the compiler wouldn't know that `T` has a `display` method.

Bounds work on classes and data types too:

```vary
class SortedPair[T: Comparable](a: T, b: T) {
    let first: T = a
    let second: T = b
}
```

If the type argument doesn't satisfy the bound, the compiler reports an error:

```text
Type 'Int' does not satisfy bound 'Printable' on parameter 'T'
```

## Built-in generic types

The standard library uses generics throughout:

| Type | Description |
|------|-------------|
| `List[T]` | Ordered, mutable collection |
| `Dict[K, V]` | Key-value mapping |
| `Set[T]` | Unordered unique elements |
| `Result[T, E]` | Success or failure |
| `Task[T]` | Async task handle |

```vary
let nums: List[Int] = [1, 2, 3]
let ages: Dict[Str, Int] = {"Alice": 30}
let tags: Set[Str] = {"a", "b"}
```
