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.
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.
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.