Scala: Ordering contravariance

风格不统一 提交于 2019-12-20 20:21:21

问题


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.


回答1:


A bit more of a detailed answer pulled out of the thread on 'scala-language' linked in the comment above.

The reason that Ordering is not contravariant is that this doesn't accord sensibly with the notion of specificity used in implicit resolution. Implicit resolution will try to pick the most 'specific' type for a parameter, and considers one type to be more specific than another if it's a subtype of it. This makes sense in covariant cases: I'd rather have an implicit specific to my list of strings than one for any old list. In contravariant cases, however, it wants to pick the wrong thing:

trait Ord[-A]
A <: B
Ord[B] <: Ord[A]

And so it will pick the 'most specific' ordering as being, if available, Ordering[Any].

There looks to be a big discussion going on changing the way 'specificity' is defined with regard to contravariant parameters on the scala-language group.




回答2:


In the current API, these methods prevent it from being contravariant:

  /** Return `x` if `x` >= `y`, otherwise `y`. */
  def max(x: T, y: T): T = if (gteq(x, y)) x else y

  /** Return `x` if `x` <= `y`, otherwise `y`. */
  def min(x: T, y: T): T = if (lteq(x, y)) x else y


来源:https://stackoverflow.com/questions/16057973/scala-ordering-contravariance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!