DEV Community

Vitor Nunes
Vitor Nunes

Posted on • Edited on

9 6

Contravariance in the real world

Contravariance hurts the brain! The definition is straightforward, but applying it to the real world doesn’t make much sense. How come Container[Waste] be a subtype of Container[Paper] when Paper is a more specialized type of Waste?

The key to understanding contravariance is to stop thinking of types in terms of "is a more specialized type" and switch the focus to the idea of acceptance.

Visualize the following scenario in Scala:

class Waste
class Paper extends Waste
class Glass extends Waste

class Container[T] {
  //...
}

object Office {
    def setPaperContainer(container: Container[Paper]): Unit = ???

    def setGlassContainer(container: Container[Glass]): Unit = ???
}
Enter fullscreen mode Exit fullscreen mode

The Office only accepts specific types of containers:Container[Paper] and Container[Glass]. But that’s not practical! What happens, in reality, is to have the same type of container with different colors:

Waste containers

They are not specialized as the example suggests.

The following expresses better what happens in reality:

object Office {
  def setPaperContainer(container: Container[Waste]): Unit = ???

  def setGlassContainer(container: Container[Waste]): Unit = ???
}
Enter fullscreen mode Exit fullscreen mode

But, what if policies in the office require a particular container for Paper that shreds everything? Would it make sense to let Container be covariant? Could the Office accept Container[Waste], or any other more specialized type for both Paper and Glass?

class Container[+T] { //<---- Covariant
  //...
}

object Office {
  def setPaperContainer(container: Container[Waste]): Unit = ???

  def setGlassContainer(container: Container[Waste]): Unit = ???
}
Enter fullscreen mode Exit fullscreen mode

The answer is NO. Otherwise, the Office would accept a shredder as a Container[Glass] and the result would be messy!

So, how to express that a Container can be either the basic type or only specialized for a given type? In other words, how to accept a Container[Waste] and a Container[Paper] for Paper, and just accept a Container[Waste] and Container[Glass] for Glass?

Using contravariance!

class Container[-T] {//<---- Contravariant
  //...
}

object Office {
  def setPaperContainer(container: Container[Paper]): Unit = ???

  def setGlassContainer(container: Container[Glass]): Unit = ???
}
Enter fullscreen mode Exit fullscreen mode

By making Container contravariant, the Office accepts a Container[Waste] for both Paper and Glass, or a specialized type for each one. And it doesn't accept a shredder for Glass, or a specialized Glass container for Paper.

Don't crush glasses in a paper shredder. Use contravariance!

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (5)

Collapse
 
courier10pt profile image
Bob van Hoove

Thanks for bringing this up, it never quite stuck with me.

Here's a C# example using the 'accepts' phrasing:

// Covariant:
//
//         generic    (accepts)   specific
//
IEnumerable<object> objects = new string[] { "a", "b", "c" }; 


// Contravariant:
//
//    specific      (accepts)       generic
//
Action<string> printIt = new Action<object>(o => Console.WriteLine(o));

Pretty much copied from MSDN : Covariance and Contravariance FAQ

Collapse
 
vitornovictor profile image
Vitor Nunes

I'm glad it helped :)
Thanks for sharing this example!

Collapse
 
tenhobi profile image
Honza Bittner

This article helped me A LOT! Thanks. Awesome example.

Collapse
 
toniogela profile image
Antonio Gelameris

Brilliant!

Collapse
 
theodesp profile image
Theofanis Despoudis

Epic I think that made it clear

Heroku

This site is powered by Heroku

Heroku was created by developers, for developers. Get started today and find out why Heroku has been the platform of choice for brands like DEV for over a decade.

Sign Up

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay