soupybionics - 1 month ago 6
Scala Question

# Contravariance and covariance in Scala

``````abstract class Bhanu[-A] { val m:List[A] }
``````

gives

``````error: contravariant type A occurs in covariant position in type => List[A] of value m
abstract class Bhanu[-A] { val m:List[A] }
``````

whereas

``````abstract class Bhanu[+A] { val m:List[A] }
``````

gives

``````defined class Bhanu
``````

I am not able to wrap my head around this concept as to why it fails for contravariance whereas it succeeds for covariance.

Secondly (from some other example),

What does the statement exactly mean?

``````Function1[Sport,Int] <: Function1[Tennis,Int] since Tennis <: Sport
``````

It seems counter-intuitive to me, Shouldn't it be the following?

``````Function1[Tennis,Int] <: Function1[Sport,Int] since Tennis <: Sport
``````

Let's look on the first example you mentioned. Consider we have:

``````class Fruit
class Apple extends Fruit
class Banana extends Fruit

class Bhanu[-A](val m: List[A]) // abstract removed for simplicity
``````

Since `Bhanu` is contravatiant `Bhanu[Fruit] <: Bhanu[Apple]` so you can do the following:

``````val listOfApples = new List[Apple](...)
val listOfFruits = listOfApples // Since Lists are covariant in Scala
val a: Bhanu[Fruit] = new Bhanu[Fruit](listOfFruits)
val b: Bhanu[Banana] = a // Since we assume Bhanu is contravariant
val listOfBananas: List[Banana] = b.m
val banana: Banana = listOfBananas(0) // TYPE ERROR! Here object of type Banana is initialized
// with object of type Apple
``````

So Scala compiler protects us from such errors by restriction to use contravariant type parameters in covariant position.

For your second question let's also look at the example. Consider we have function:

``````val func1: Function1[Tennis,Int] = ...
``````

If `Function1[Tennis,Int] <: Function1[Sport,Int]` where `Tennis <: Sport` as you proposed than we can do the following:

``````val func2: Function1[Sport,Int] = func1
val result: Int = func2(new Swimming(...)) // TYPE ERROR! Since func1 has argument
// of type Tennis.
``````

But if we make `Function1` contravariant in its argument so `Function1[Sport,Int] <: Function1[Tennis,Int]` where `Tennis <: Sport` than:

``````val func1: Function1[Tennis,Int] = ...
val func2: Function1[Sport,Int] = func1 // COMPILE TIME ERROR!
``````

and everything is fine for the reverse case:

``````val func1: Function1[Sport,Int] = ...
val func2: Function1[Tennis,Int] = func1 // OK!
val result1: Int = func1(new Tennis(...)) // OK!
val result2: Int = func2(new Tennis(...)) // OK!
``````

Functions must be contravariant in their argument type and covariant in result type:

``````trait Function1[-T, +U] {
def apply(x: T): U
}
``````
Source (Stackoverflow)