# Variance of type parameter in Ordered

## class A(val x: Int) extends Ordered[A] { override def compare(that: A): Int = this.x - that.x } case class B(y: Int) extends A(y) { } val b1 : B = new B(1) val b2 : B = new B(2) if (b1.compare(b2) < 0) { println(“b1 < b2”) } else { println(“b1 >= b2”) }

As can be seen, it is possible to compare Bs although the only implement Ordered[A]. This is a contra-variant relation, but trait Ordered is defined in a invariant way:

trait Ordered[A] extends Any with java.lang.Comparable[A]

I tried to find out in the documentation and the Programming in Scala book, but was not able to find an answer. Does somebody know?

Because Java? For whatever reasons, `Ordered[A]` extends `Comparable[A]`, so it needs to be invariant.

``````trait VOrdered[-A] extends Any with Comparable[A] :
def vcompare(that: A): Int
``````
``````contravariant type A occurs in invariant position in type Any with Comparable[A] {...} of trait VOrdered
``````

Without this legacy baggage it should be fine.

``````trait VOrdered[-A] extends Any :
def vcompare(that: A): Int

trait Numbered extends Any with VOrdered[Numbered] :
def number: Int
def vcompare(that: Numbered): Int = number.compare(that.number)

case class PlainNumbered(number: Int) extends Numbered

def asContra(n: Int): VOrdered[PlainNumbered] = PlainNumbered(n)

println(asContra(42).vcompare(PlainNumbered(43))) // -1
``````

Another reason is that variance and typeclasses don’t play well together.

So, it is usually better just to have a `contraMap` & `narrow` methods.

1 Like

`Ordered[A]` is plain OO inheritance. The type-classy counterpart (that I’d definitely prefer to use) would be `Ordering[A]`, but this similarly ties in with `java.lang.Comparator[A]`.

1 Like

Thanks for the explanations, that sounds reasonable. However, why does my example with the Bs still work? In Java we would have to use a contra-variant use-site variance annotation, which would be in Scala e.g. [T <: Ordered[_ >: T]]:

def max[T <: Ordered[_ >: T]](t1: T, t2: T) = if (t1 >= t2) then t1 else t2

But in Scala it also works without it, just with [T <: Ordered[T]]

def max[T <: Ordered[T]](t1: T, t2: T) = if (t1 >= t2) then t1 else t2

There must be some other ‘magic’.

Oh right, I mixed both (again), my bad.
I really never ever used `Ordered`, its usage is too cumbersome.

That is just plain-old subtyping, nothing fancy is happening there.

Actually, thinking about it, there isn’t much about contravariance in any of the examples and it would be weird in any case.

I tried to make an example to show how to use it in a contravariant way, but I couldn’t make it compile in any way; which makes sense because what I was trying to do is unsafe. `Ordered` is simply a badly designed abstraction, I wouldn’t bother about it.

Right. I just assumed that everything just works in terms of super type `A`.

However…

…now I’m somewhat confused.

``````summon[B <:< Ordered[A]] // as expected: compiles
// summon[B <:< Ordered[B]] // as expected: Cannot prove that B <:< Ordered[B].

val b1: Ordered[B] = new B(1) // compiles O_o
``````

Where am I going wrong - why does the `Ordered[B]` part work?

I would bet some form of implicit conversion back and forth.

There is something called `reify` or something like that, which you can use in the REPL to see what is happening.
Sorry for just telling what to do rather than doping it myself, I can’t at the moment

Of course, thanks - should’ve thought of checking rogue implicits myself in the first place.

In its full, desugared beauty:

``````val b1: Ordered[B] =
Ordered.orderingToOrdered(new B(1))(Ordering.ordered(\$conforms))
``````

1 Like

re: `Ordering` (as opposed to `Ordered`), some previous discussion is at Ordering[T] and friends should be contravariant · Issue #7179 · scala/bug · GitHub

1 Like

Thanks, this gives some insight.