# Generics, Variance and Bounds — Scala

Source: https://www.geekswithgeeks.com/en/scala/t-generics

> Write generic code and understand covariance, contravariance and bounds.

## Generic types done right

Classes, traits and methods take **type parameters** in square brackets: `class Box[A](value: A)`, `def firstOr[A](xs: List[A], default: A): A`. **Variance** describes how subtyping of type arguments relates to subtyping of the generic type. **Covariant** `+A` means `List[Dog]` is a `List[Animal]`, which is safe for immutable producers such as `List`, `Option` and `Vector`. **Contravariant** `-A` means a `Printer[Animal]` can be used where a `Printer[Dog]` is expected, suitable for consumers such as function parameters (`Function1[-A, +B]`). **Invariant** `A` (the default) allows neither, which is required for mutable containers such as `Array` and `mutable.Buffer`. **Upper bounds** `A <: Shape` restrict a type parameter to subtypes, and **lower bounds** `B >: A` allow methods like `prepend[B >: A](b: B): List[B]` to widen the element type safely. Higher-kinded types (`F[_]`) abstract over containers themselves and underpin libraries such as Cats. The compiler checks variance positions so you cannot accidentally make an unsafe type covariant.

## Variance at a glance

Covariant types follow subtyping, contravariant types reverse it, invariant types ignore it.

![Three pairs of boxes with arrows: one pair with arrows in the same direction, one reversed, and one with no arrow between them.](assets/figures/scala/section-5-map.svg) — Figure 5.1 — Covariance, contravariance and invariance.

## Variance and bounds

A covariant result type and a contravariant handler.

```scala
sealed trait Animal { def name: String }
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal

sealed trait Result[+A]                              // covariant: Result[Dog] <: Result[Animal]
case class Ok[+A](value: A) extends Result[A]
case class Err(message: String) extends Result[Nothing]  // Nothing fits any Result[A]

trait Handler[-A]:                                    // contravariant: consumes A
  def handle(a: A): Unit

val animalLogger: Handler[Animal] = a => println(s"saw ${a.name}")
val dogHandler: Handler[Dog] = animalLogger            // OK: handles any animal, so handles dogs

val fetched: Result[Animal] = Ok(Dog("Bruno"))         // OK thanks to +A

def loudest[A <: Animal](animals: List[A]): Option[A] = // upper bound
  animals.maxByOption(_.name.length)

val pets: List[Animal] = Cat("Mitthu") :: List(Dog("Bruno"))   // :: widens via a lower bound
println(loudest(List(Dog("Bruno"), Dog("Sheru"))))
```

## A quick variance rule

If a type only produces values of A (returns them), it can be covariant. If it only consumes A (takes it as input), it can be contravariant. If it does both, as mutable collections do, keep it invariant.

**Quiz:** Why is Array[A] invariant in Scala?

- [x] Because arrays are mutable: a covariant array would allow writing a Cat into an Array[Dog]
- [ ] Arrays cannot hold objects
- [ ] Because of performance
- [ ] It is covariant

*Answer:* Because arrays are mutable: a covariant array would allow writing a Cat into an Array[Dog]. Mutable containers both produce and consume elements, so covariance would be unsafe.
