Is +A or -A? right?

Could someone please explain to me the difference between covariance and contravariance?
I understand invariance.

1 Like

The +A is covariant and the -A is contravariant. This alters the subtyping relationship between the two parameterized types of the main type based on the type parameter. To help make this discussion concrete, let’s assume a class Foo[A] to start with, them change it to class Foo[+A] and class Foo[-A].

With class Foo[A], Foo is invariant with A. What that means is that Foo[Type1] and Foo[Type2] are completely unrelated types, regardless of whether a subtyping relationship exists between Type1 and Type2. Where this surprises people is that if Apple is a subtype of Fruit, Foo[Apple] will not be a subtype of Foo[Fruit] and you can’t pass an instance of Foo[Apple] into a function that expects Foo[Fruit]. That is the easy one, which you have said you understand, but it is good to make it clear.

The declaration class Foo[+A] makes Foo covariant with A. In this situation, Foo[Type1] is a subtype of Foo[Type2] iff Type1 is a subtype of Type2. That means that now we can pass a Foo[Apple] into a function that expects a Foo[Fruit]. The List type in Scala in covariant, so you can do this. You might wonder why you have things that aren’t covariant given that covariance just seems to make sense. The answer is that if the Foo type allows you to pass in things of type A, then being covariant can cause problems. Consider an Array instead of a List. Because Array is mutable if I were allowed to pass an Array[Apple] into a function that accepts an Array[Fruit], that function could add some other type of Fruit, say a Banana into the Array. That can’t be allowed with an Array[Apple], and that is why Array can’t be covariant. In a simple (and incomplete) sense, covariant parameters can’t be passed into methods, of the type, but they can be returned. So you can get values from a List, but you can’t set them.

If you declare class Foo[-A] then Foo is contravariant with A. This is the one that I think people generally find less intuitive, and it isn’t used as much. In this case, Foo[Type1] is a subtype of Foo[Type2] iff Type1 is a supertype of Type2. That means that Foo[Fruit] is a subtype of Foo[Apple]. That might not seem to make sense, but it is exactly what you want when you have methods where values of that type are passed in, but not returned. If I had a write-only collection that I could just add stuff to, call it a WList, then it would make sense to use a WList[Fruit] in a place where a WList[Apple] was expected. It would be safe because the function that expects a WList[Apple] can only add apples and it is safe to add apples to a WList[Fruit]. There is a classic example in the Scala libraries in the form of Function1[-T1, +R]. One function type is a subtype of another type if the input argument is a supertype and the return type is a subtype. Running with the fruit example, Apple => Fruit is a subtype of Fruit => Apple because if I expect a function of the type Apple => Fruit I will only give it apples and my expectation is that it will return fruit. Given this, it is perfectly safe for my to pass the apple to a Fruit => Apple and get back an apple that I use as a general fruit.

3 Likes

Another example is Function[-Z, +Y]

If the contract is to require an apple to provide any car
then you should not complain if you receive a sports car when giving any fruit

Hello,

The drawing alone makes this post worth checking out:
http://blog.originate.com/blog/2016/08/10/cheat-codes-for-contravariance-and-covariance/

Best, Oliver

1 Like

“…but afterwards the nightmares got worse, since now they included clowns and burning pandas. I stayed up for days.” :slight_smile: