Skip to content

Instantly share code, notes, and snippets.

@pathikrit
Last active November 2, 2019 22:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pathikrit/a7845f72645159646fdb632bc334771a to your computer and use it in GitHub Desktop.
Save pathikrit/a7845f72645159646fdb632bc334771a to your computer and use it in GitHub Desktop.
Contravariance vs. Covariance
  • Let C<A> be a higher-kinded type e.g. in List<Animal>, List is C and Animal is A.
  • Let S be a subtype of T e.g. in class Cat extends Animal, Cat is S and Animal is T
  • If C<S> is a subtype of C<T>, then C is covaraint on T e.g. List<Cat> is a subtype of List<Animal>
  • If C<T> is a subtype of C<S>, then C is contravariant on T e.g. Predicate<Animal> is a subtype of Predicate<Cat>
  • If neither C<T> and nor C<S> are subtypes of the other, thenC is invariant on T
  • If both C<T> and C<S> are subtypes of each other, then C is phantom variant on T. This is possible in languages which support phantom types like Haskell

In Scala:

  • Coavariance is denoted by prefixing the type argument using a + e.g. trait List[+A]
  • Contravariance is denoted by prefixing the type argument using a - e.g. trait Predicate[-A]
  • Invariance is simply denoted by default without any prefixes e.g. trait Container[A]
  • We can have all covariance, contravariance and invariance in a single higher-kinded type e.g. functions are contravariant on the input but covariant on the output i.e. trait Function1[-I, +O]
  • Phantom Variance is impossible in Scala since it does not support phantom types.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment