I have defined two typeclasses:
trait WeakOrder[-X] { self =>
def cmp(x: X, y: X): Int
def max[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) >= 0) x else y
def min[Y <: X](x: Y, y: Y): Y = if (cmp(x, y) <= 0) x else y
}
trait Lattice[X] { self =>
def sup(x: X, y: X): X
def inf(x: X, y: X): X
}
I would like to do the following:
trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
def sup(x: X, y: X): X = max(x, y)
def inf(x: X, y: X): X = min(x, y)
}
But this is impossible because contravariant type X
appears at a covariant position (the returning value of sup
and inf
).
However, semantically this is correct: max
and min
with the type signature max[Y <: X](x: Y, y: Y): Y
encodes the fact that the returning value of max
/ min
must be one of the two arguments.
I tried to do the following:
trait TotalOrder[-X] extends Lattice[X] with WeakOrder[X] { self =>
def sup[Y <: X](x: Y, y: Y): Y = max(x, y)
def inf[Y <: X](x: Y, y: Y): Y = min(x, y)
}
However, the method def sup[Y <: X](x: Y, y: Y): Y
cannot inherit def sup[X](x: X, y: X): X
. The compiler complains that the type signature does not match. But the former one (with the on-site variance annotation) imposes a stronger type restrictions than the latter signature. Why the former one cannot inherit the latter one? How can I bypass the contravariant type restrictions on TotalOrder[-X]
(semantically, a total order is contravariant)?