# > 140

## Contravariant Traits in Scala or “Help Rahul”

In English, Programming on November 15, 2010 at 9:48 pm

So here is my modest attempt to tell the world about contravariants.
Edit: and it’s “a little bit” longer than I thought it would be 😉

If you’re Rahul then this is specially for you.
If you’re not Rahul but interested in contravariants in scala, regard yourself as Rahul and this is specially for you.
Sorry about the “blog”. Guess soup.io wasn’t really cut for comments, posting source code and stuff. But it will do the trick. Feel free to contact me via Twitter if you have any questions. I will try to link them to this post. EDIT: OK this is the reason I moved to wordpress 😉

And of we go:
let’s start by defining 3 little classes

`class SuperLittleProfessor(val i:Int)`

```class LittleProfessor(tmp:Int) extends SuperLittleProfessor(tmp) { def add(x:Int) = i + x }```

```class SubLittleProfessor(tmp:Int) extends LittleProfessot(tmp) { def mult(x:Int) = i * x }```

Pretty straight forward. Let us repeat what inheritance gives us: We can modify or enhance given classes and we can use the new class where ever we would have used the old one. Let me stress this again because we are going to need it: If class `B` extends class `A` then we can/want/should be able to use it where ever we would have used or will use `A`.

Got that?
Good.

Now we will introduce a little trait. I’m starting with an covariant one, because that’s much easier to grasp. We’ll get to a contravariant one later:

```trait Creator[+A] { def create(i:Int):A }```

Again no rocket science here. Now what does the little “`+`” tell us? It says that if have a type `T1` and a subtype `T2` that an instance of `Creator[T2]` is automatically a subtype of of `Creator[T1]` and we can use it everywhere we would have used `Creator[T1]`.

“Wait a second” I here you say “why can we do that?”.

Well see it like this: if we call `create()` on an instance of `Creator[T1]` we are getting a `T1`. If we call `create()` on an instance of `Creator[T2]` we are getting a `T2`. `T2` is a subtype of `T1` which can be used everywhere we would have used `T1`. Hence we can use `Creator[T2]` where ever we would have used `Creator[T1]`.

Get it?
Lets try it in REPL
```scala> class LilProfCreator extends Creator[LittleProfessor] { |   def create(i:Int) = new LittleProfessor(i) | } defined class LilProfCreator```

```scala> class SubLilProfCreator extends Creator[SubLittleProfessor] { |   def create(i:Int) = new SubLittleProfessor(i) | } defined class SubLilProfCreator```

```scala> val a = List(new LilProfCreator) a: List[LilProfCreator] = List(LilProfCreator@61a0353d) ```

```scala> a :+ new SubLilProfCreator res1: List[ScalaObject with Creator[LittleProfessor]] = List(LilProfCreator@61a0353d, SubLilProfCreator@5655d1b4)```

See that? We add a `Creator[SubLittleProfessor]` to a `List[LilProfCreator]` (which could be replaced by `List[Creator[LittleProfessor]])` and it all worked. In other words: we used a `Creator[SubLittleProfessor]` where we would have expected a `Creator[LittleProfessor]`.

Nice! And intuitive isn’t it? Believe me: contravariants are as intuitive, once you’re looking at them the right way.

```trait User[-A] { def use(a:A):Int }```

Now what does the “-” say? It says that `User[T2]` is a subtype of `User[T1]` if `T2` is a supertype of `T1` . . .

WHAAAAAAAAAAAAAAAAAAAT?

Stay calm! Let’s step 3 steps back and take a look at `Creator` again.
There’s got to be a hint right?
Yes there is.

In `Creator` the type parameter appeared as result of a function.
In `User` the type parameter appears as parameter of a function.

Now let’s step back even one more step: I deliberately chose the structure of the `LittleProfessor` system so that every subclass adds a new feature. We were able to substitute `Creator[LittleProfessor]` for a `Creator[SubLittleProfessor]` because `SubLittleProfessor` is the more advanced type with more features. In a place where we’d expect a `LittleProfessor` we wouldn’t mind the extra `mult()` function that `SubLittleProfessor` offers us.

Now with `User` and the function `use()` that receives something from us we have to loosen that constraint!
In other words: In oder to let us use `User[T2]` where we would expect a `User[T1]` we have to demand LESS or equal features from `T2` than from `T1`. This way we can feed a `T1` to the function `use()` of `User[T2]`. It sure won’t mind the extra features. But we can’t feed a `T2` to a `User[T1].use()` since it might demand a feature that `T2` doesn’t have.

Or as I put it above: `User[T2]` is a SUBtype of `User[T1]` if `T2` is a SUPERtype of `T1`

Lets try it again in REPL

```scala> class LilProfUser extends User[LittleProfessor] { |   def use(a:LittleProfessor) = a add 1 | } defined class LilProfUser```

```scala> class SubLilProfUser extends User[SuperLittleProfessor] { |   def use(a:SuperLittleProfessor) = a.i | }defined class SubLilProfUser```

```scala> val b = List(new LilProfUser()) b: List[LilProfUser] = List(LilProfUser@3eb217d5)```

```scala> b :+ new SuperLilProfUser() res2: List[ScalaObject with User[LittleProfessor]] = List(LilProfUser@3eb217d5, SuperLilProfUser@5dcdd76a)```

So that’s all there is to it. You need to enforce the constraints on type parameters that appear as results and loosen the constraints on type parameters that appear as parameters if you want to benefit from automatic inheritance.

Of course that also implies 2 things: invariant type parameters may only appear as results contravariant type parameters may only appear as parameters.
Otherwise you get a compiler error.
EDIT: This again implies two other things
– a `val` can only be convariant
– a `var` can only be invariant since it can’t be convariant and contravariant at the same time

Any questions left?

1. Thanks for the article. How would you explain the difference between +A and T <: A ?

2. wow! my very first comment on my very first blog 😉

sorry, that I had to approve your comment first. Changed that setting now.

As for me explaining stuff: I’m still waiting for Martin Odersky to show up and tell me that I got it all wrong ;). So whatever I say . . . please cross-check with “more official” sources.
That being I said, I’ll give it a shot:

It is my understanding that these two are actually very distinct and have no overlapping.

Let’s say we have two classes A and B (with B being a subclass of A), some random Trait R and one more interesting Trait T[X].

Now T[X <: R] introduces a constraint. You can only use subtypes of R as typeparameter. But it does not tell us anything about the relation between T[A] and T[B] i.e. T[B] is NOT a subtype of T[A] (or the other way round)

On the other hand I like to see T[+A] (and T[-A] too) as "unlocking a feature". This does in no way constrain the types we can use as type parameter but tells us that T[B] will be a subtype of T[A] (of course this "unlocked" feature comes with constraints . . . which I described in the article).

So you see they have nothing in common and actually you can even use them together. I’m only working in Scala for about 2 month now and I already have encountered at least 3 cases where I wrote a type signature like T[+X <: A]

Any questions?