I just learned Scala. Now I am confused about Contravariance and Covariance.
From this page, I learned something below:
Covariance
Perhaps the most obvious feature of subtyping is the ability to replace a value of a wider type with a value of a narrower type in an expression. For example, suppose I have some types Real
, Integer <: Real
, and some unrelated type Boolean
. I can define a function is_positive :: Real -> Boolean
which operates on Real
values, but I can also apply this function to values of type Integer
(or any other subtype of Real
). This replacement of wider (ancestor) types with narrower (descendant) types is called covariance
. The concept of covariance
allows us to write generic code and is invaluable when reasoning about inheritance in object-oriented programming languages and polymorphism in functional languages.
However, I also saw something from somewhere else:
scala> class Animal
defined class Animal
scala> class Dog extends Animal
defined class Dog
scala> class Beagle extends Dog
defined class Beagle
scala> def foo(x: List[Dog]) = x
foo: (x: List[Dog])List[Dog] // Given a List[Dog], just returns it
scala> val an: List[Animal] = foo(List(new Beagle))
an: List[Animal] = List(Beagle@284a6c0)
Parameter x
of foo
is contravariant
; it expects an argument of type List[Dog]
, but we give it a List[Beagle]
, and that's okay
[What I think is the second example should also prove Covariance
. Because from the first example, I learned that "apply this function to values of type Integer
(or any other subtype of Real
)". So correspondingly, here we apply this function to values of type List[Beagle]
(or any other subtype of List[Dog]
). But to my surprise, the second example proves Cotravariance
]
I think two are talking the same thing, but one proves Covariance
and the other Contravariance
. I also saw this question from SO. However I am still confused. Did I miss something or one of the examples is wrong?