ielkhalloufi
ielkhalloufi

Reputation: 682

Contravariance in scala

I am new with scala. I am trying to figure out how the whole contravariance relationship works. I understand the concept of covariance and invariant and I also know how I would implement them in practise. I also understand the concept of contravariance (the reverse of covariance) and how it is implemented in the Function1 trait in Scala. It gives you an abstraction without redefining Function1 implementations for different classes. But, I still don’t get it completely, strange? Now, I am almost there… how can I solve the following problem with contravariance:

class GarbageCan[-A] {

  def doSomething(a: A): Unit ={
    // do something with 'a' of subtype that is not possible with the supertype
  }

}

def setGarbageCanForPlastic(gc: GarbageCan[PlasticItem]): Unit = {

}

The above example is extracted from http://blog.kamkor.me/Covariance-And-Contravariance-In-Scala/. A really good explanation concerning this subject. The hierarchy is as follows: Item (base class) -> PlasticItem (subclass) -> PlasticBottle (subclass of subclass)

The setGarbageCanForPlastic function accepts a GarbageCan with the type of PlasticItem. Because the Parameterized type is contravariant, the following statement is completely legal:

setGarbageCanForPlastic(new GarbageCan[Item])

Now, the doSomething function accepts a Type parameter which is contravariant. How can I work with this type, if I don’t know if the type is a Base class “Item” or subclass “PlasticItem”? I can do something which is permissible within the subclass and not in the base class. If this was an covariant parameter, this would be no problem, a subclass inherits everything from the base class.

I lost it right?... Hope somebody can help me out.

Upvotes: 3

Views: 150

Answers (1)

FPstudent
FPstudent

Reputation: 841

First of all, the doSomething method actually cannot do anything other than essentially discarding a since it does not know what A is. To make it more useful, you would need a bound like class GarbageCan[-A <: Item].

Now, suppose setGarbageCanForPlastic(gc) calls gc.doSomething(new PlasticItem). Since A in GarbageCan[A] is contravariant, we have GarbageCan[Item] <: GarbageCan[PlasticItem] <: GarbageCan[PlasticBottle]. Indeed, the function call setGarbageCanForPlastic(new GarbageCan[Item])) is safe as GarbageCan[Item]'s doSomething can process any Item including a PlasticItem, while the call setGarbageCanForPlastic(new GarbageCan[PlasticBottle])) is unsafe because GarbageCan[PlasticBottle]'s doSomething may not be able to take a PlasticItem which is not necessarily a PlasticBottle.

Upvotes: 1

Related Questions