Dzhu - 1 year ago 126

Scala Question

I understand covariance and contravariance in scala. Covariance has many applications in the real world, but I can not think of any for contravariance applications, except the same old examples for Functions.

Can someone shed some light on **real world examples** of

`contravariance`

Answer Source

In my opinion, the two most simple examples after `Function`

are ordering and equality. However, the first is not contra-variant in Scala's standard library, and the second doesn't even exist in it. So, I'm going to use Scalaz equivalents: Order and Equal.

Next, I need some class hierarchy, preferably one which is familiar and, of course, it both concepts above must make sense for it. If Scala had a `Number`

superclass of all numeric types, that would have been perfect. Unfortunately, it has no such thing.

So I'm going to try to make the examples with collections. To make it simple, let's just consider `Seq[Int]`

and `List[Int]`

. It should be clear that `List[Int]`

is a subtype of `Seq[Int]`

, ie, `List[Int] <: Seq[Int]`

.

So, what can we do with it? First, let's write something that compares two lists:

```
def smaller(a: List[Int], b: List[Int])(implicit ord: Order[List[Int]]) =
if (ord.order(a,b) == LT) a else b
```

Now I'm going to write an implicit `Order`

for `Seq[Int]`

:

```
implicit val seqOrder = new Order[Seq[Int]] {
def order(a: Seq[Int], b: Seq[Int]) =
if (a.size < b.size) LT
else if (b.size < a.size) GT
else EQ
}
```

With these definitions, I can now do something like this:

```
scala> smaller(List(1), List(1, 2, 3))
res0: List[Int] = List(1)
```

Note that I'm asking for an `Order[List[Int]]`

, but I'm passing a `Order[Seq[Int]]`

. This means that `Order[Seq[Int]] <: Order[List[Int]]`

. Given that `Seq[Int] >: List[Int]`

, this is only possible because of contra-variance.

The next question is: does it make any sense?

Let's consider `smaller`

again. I want to compare two lists of integers. Naturally, anything that compares two lists is acceptable, but what's the logic of something that compares two `Seq[Int]`

being acceptable?

Note in the definition of `seqOrder`

how the things being compared becomes *parameters* to it. Obviously, a `List[Int]`

can be a parameter to something expecting a `Seq[Int]`

. From that follows that a something that something that compares `Seq[Int]`

is acceptable in place of something that compares `List[Int]`

: they both can be used with the same parameters.

What about the reverse? Let's say I had a method that only compared `::`

(list's cons), which, together with `Nil`

, is a subtype of `List`

. I obviously could not use this, because `smaller`

might well receive a `Nil`

to compare. It follows that an `Order[::[Int]]`

cannot be used instead of `Order[List[Int]]`

.

Let's proceed to equality, and write a method for it:

```
def equalLists(a: List[Int], b: List[Int])(implicit eq: Equal[List[Int]]) = eq.equal(a, b)
```

Because `Order`

extends `Equal`

, I can use it with the same implicit above:

```
scala> equalLists(List(4, 5, 6), List(1, 2, 3)) // we are comparing lengths!
res3: Boolean = true
```

The logic here is the same one. Anything that can tell whether two `Seq[Int]`

are the same can, obviously, also tell whether two `List[Int]`

are the same. From that, it follows that `Equal[Seq[Int]] <: Equal[List[Int]]`

, which is true because `Equal`

is contra-variant.