scala-generics · git:20260424.d50cf5a · 2026-04-24 · sha256 313cbb97c7e517c8
scala-generics git:20260424.d50cf5aA
Immutable. This exact content never changes and is served at /api/v1/blob/313cbb97c7e517c8.
---
name: scala-generics
description: Translating Python generic types, variance, and type bounds to Scala 2.13
---
# Scala Generics and Type System for Python Developers
## Core Concepts
### Variance (Key Difference from Python)
Python has no explicit variance system. Scala requires explicit declarations:
**Covariance (+T)** - "out" position, can return subtypes:
```scala
class Container[+T](items: Seq[T]) {
def get: T = items.head // OK - returning subtype
// def set(t: T): Unit // ERROR - would be contravariant
}
```
**Contravariance (-T)** - "in" position, can accept supertypes:
```scala
class Consumer[-T] {
def consume(t: T): Unit // OK - accepting supertype
// def produce: T // ERROR - would be covariant
}
```
**Invariance (T)** - exact type, no variance:
```scala
class Handler[T] {
def get: T
def set(t: T): Unit // Both OK - invariant
}
```
### Translation Pattern
| Python | Scala | Notes |
|--------|-------|-------|
| `TypeVar("T")` | `[T]` | Invariant by default |
| `TypeVar("T_co", covariant=True)` | `[+T]` | Covariant, out-position only |
| `TypeVar("T_contra", contravariant=True)` | `[-T]` | Contravariant, in-position only |
| `TypeVar("T", int, float)` | `[T <: Int \| Float]` | Upper bound (Scala 3) or sealed trait |
| `Generic[T]` | `[T]` | Class definition |
| `Union[A, B]` | `A \| B` (Scala 3) or sealed trait | Use sealed traits for compatibility |
### Type Bounds
```scala
// Upper bound - T must be subtype of Ordered
class Comparable[T <: Ordered[T]]
// Lower bound - T must be supertype of String
class Container[T >: String]
// Context bound (implicit evidence)
class Serializable[T: Format]
```
### Higher-Kinded Types (Scala's advantage)
Python's `TypeVar("F")` for type constructors cannot express true HKTs.
Scala can use type lambdas:
```scala
// Scala 2.13 with kind-projector
type Functor[F[_]] = {
def map[A, B](fa: F[A], f: A => B): F[B]
}
// Or in Scala 3
def map[F[_], A, B](fa: F[A], f: A => B): F[B]
```
## Translation Examples
### Generic Container (Python)
```python
class TokenContainer(Generic[T_co]):
def __init__(self, items: Sequence[T_co]) -> None:
self._items: tuple[T_co, ...] = tuple(items)
def get_all(self) -> tuple[T_co, ...]:
return self._items
```
### Generic Container (Scala 2.13)
```scala
class TokenContainer[+T](items: Seq[T]) {
private val _items: Vector[T] = items.toVector
def getAll: Vector[T] = _items
def size: Int = _items.size
}
```
## Collections Generics
Python uses `Sequence[T]`, `Iterable[T]`, etc. Scala equivalents:
| Python | Scala |
|--------|-------|
| `Sequence[T]` | `Seq[T]` (immutable) |
| `Iterable[T]` | `Iterable[T]` |
| `Iterator[T]` | `Iterator[T]` |
| `list[T]` | `scala.collection.mutable.ListBuffer[T]` or `List[T]` (immutable) |
| `tuple[T, ...]` | `Vector[T]` or `(T, T, ...)` for fixed size |
| `dict[K, V]` | `Map[K, V]` |
## Best Practices for Scala 2.13
1. **Prefer immutable by default** - use `Vector`, `List`, `Map`
2. **Generics in method position** - use `[T]` not just at class level
3. **Variance where appropriate** - adds flexibility but keep simple
4. **Use sealed traits for union types** - more powerful than Python's Union
5. **Type classes for ad-hoc polymorphism** - replaces duck typing
## Working with Circe JSON Types
Circe provides `Json` type which is immutable and generic:
```scala
import io.circe._
// Json is essentially: Json = JNull | JBoolean | JNumber | JString | JArray | JObject
val json: Json = Json.fromString("hello")
val jsonObj: Json = Json.obj("key" -> Json.fromString("value"))
```