Is there any reason why Scala's Ordering
trait is not contravariant? A motivating example follows.
Suppose I want to perform an ordered insert. I may have a function with the signature
def insert[A, B >: A](list: List[A], item: A)(implicit ord: Ordering[B]): List[A]
Here, I have an Ordering
which accepts super types of type A
. I imagine this is useful when you're dealing with case classes
. For example:
abstract class CodeTree
case class Fork(left: CodeTree, right: CodeTree, chars: List[Char], weight: Int) extends CodeTree
case class Leaf(char: Char, weight: Int) extends CodeTree
def weight(tree: CodeTree): Int
def chars(tree: CodeTree): List[Char]
implicit object CodeTreeOrdering extends Ordering[CodeTree] {
def compare(a: CodeTree, b: CodeTree): Int = weight(a) compare weight(b)
}
I would want my insert function to work with types List[CodeTree]
, List[Leaf]
or List[Fork]
. However, as Ordering
isn't contravariant, I need to define implicit Orderings
for each case
.
If I define
trait MyOrdering[-A] {
def compare(a: A, b: A): Int
}
everything works as expected.
Is there any other way to achieve my goal?
EDIT:
My current solution is to define insert as
def insert[A](list: List[A], item: A)(implicit ord: Ordering[A]): List[A]
which works fine when dealing with List[CodeTree]
. I also define (inspired by the scalaz library):
trait Contravariant[F[_]] {
def contramap[A, B](r: F[A], f: B => A): F[B]
}
implicit object OrderingContravariant extends Contravariant[Ordering] {
def contramap[A, B](r: Ordering[A], f: B => A) = r.on(f)
}
implicit def orderingCodeTree[A <: CodeTree]: Ordering[A] =
implicitly[Contravariant[Ordering]].contramap(CodeTreeOrdering, identity)
I'm defining an implicit factory function for Ordering[A <: CodeTree]
instances.