Why it matters

The expressiveness has a cost: complex error messages, longer compile times, and a steep learning curve. But it also enables safer APIs and more code reuse than simpler type systems allow.

Advertisement

The architecture

Generics work like Java's but with sharper syntax and full type inference. A List[Int] is a compile-time-checked list of ints. Type parameters can be constrained with upper bounds (T <: Ordering) or lower bounds.

Variance annotations: covariance (List[+A]) means List[Cat] is a subtype of List[Animal]. Contravariance (Function1[-A, +B]) reverses the relationship for input types. Invariant (default) means no substitution.

Scala type system featuresGeneric typesT, List[T]Variance+A, -AHigher-kindedF[_], monadType classes via implicits give ad-hoc polymorphism without runtime dispatch
Type system layers.
Advertisement

How it works end to end

Higher-kinded types accept type constructors as type parameters. def show[F[_]](fa: F[Int]) works for any F: List, Option, Future, etc. This is what makes functional libraries like Cats possible.

Type classes via implicits: define a trait like Show[A] with a show method, provide implicit implementations for various A, and the compiler auto-wires them at call sites. Scala 3 replaces implicits with the cleaner 'given' syntax.

Path-dependent types tie types to instances. This enables encoding invariants that would need runtime checks in Java.