Kodumaro :: *Variance analogy

Released on August 12th, 2020
The shadows behind the code.
German Shepherd dog

The other day, talking to some coworkers, I realised developers unacquainted to functional programming find hard to understand type covariance and contravariance abstractions.

So I thought about an analogy to make it easier.

Type contravariance

Let’s define two classes that represent something easy to get in mind: dogs. Shepherds are dogs, so (in Scala):

class Dog

class Shepherd extends Dog

We have a job for a dog trainer. Think about a Shepherd pack needing to be trained.

The job title is “Shepherd trainer”, but a dog trainer – someone who know how to train every kind of dog – is competent for the job:

class Trainer[-A]

val trainerJob: Trainer[Shepher] = new Trainer[Dog]

That’s okay! But a Shepherd trainer couldn’t take a general dog trainer job, ’cause he has no experience with training other kinds of dog.

This is contravariance:

// If:
implicitly[Shepherd <:< Dog]
// Then:
implicitly[Trainer[Dog] <:< Trainer[Shepherd]]

(<:< means “is subclass of,” or: every Shepherd is a Dog too, so every Trainer[Dog] is a Trainer[Shepherd] too.)

In Scala we use - to represent contravariance.

Type covariance

Now imagine that we need to hire a dog producer. We’re not interested in what kind of dog he’ll supply, any kind is great.

So a Shepherd producer could apply for the service, ’cause Shepherd dogs are so good as any other.

class Producer[+A]

val producerContract: Producer[Dog] = new Producer[Shepherd]

It’s called covariance:

// If
implicitly[Shepherd <:< Dog]
// Then
implicitly[Producer[Shepherd] <:< Producer[Dog]]

(Yet every Producer[Shepherd] is a Producer[Dog] too – he produces only dogs.)

In Scala we use + to represent covariance.

Also in DEV Community 👩‍💻👨‍💻.

Concept | Functional | Scala

DEV Profile 👩‍💻👨‍💻