【问题标题】:Scala: Ordering contravarianceScala:排序逆变
【发布时间】:2013-04-10 02:14:30
【问题描述】:

Scala的Ordering trait 不是逆变的有什么原因吗?下面是一个鼓舞人心的例子。

假设我想执行一个有序的插入。我可能有一个带有签名的函数

def insert[A, B >: A](list: List[A], item: A)(implicit ord: Ordering[B]): List[A]

在这里,我有一个Ordering,它接受A 类型的超类型。我想这在您处理case classes 时很有用。例如:

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)
}

我希望我的插入函数可以与 List[CodeTree]List[Leaf]List[Fork] 类型一起使用。但是,由于Ordering 不是逆变的,我需要为每个case 定义隐式Orderings

如果我定义

trait MyOrdering[-A] {
  def compare(a: A, b: A): Int
}

一切都按预期进行。

还有其他方法可以实现我的目标吗?

编辑:

我目前的解决方案是将插入定义为

def insert[A](list: List[A], item: A)(implicit ord: Ordering[A]): List[A]

在处理List[CodeTree] 时效果很好。我还定义(受 scalaz 库启发):

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)

我正在为 Ordering[A &lt;: CodeTree] 实例定义一个隐式工厂函数。

【问题讨论】:

标签: scala generics types contravariance type-systems


【解决方案1】:

从上面评论中链接的“scala-language”线程中提取了更多详细的答案。

Ordering 不是逆变的原因是这不符合隐式解析中使用的特异性概念。隐式解析将尝试为参数选择最“特定”的类型,并认为一种类型比另一种类型更具体,如果它是它的子类型。这在协变情况下是有意义的:我宁愿隐含特定于我的字符串列表,而不是任何旧列表。然而,在逆变情况下,它想选择错误的东西:

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

因此它将选择“最具体”的顺序作为Ordering[Any](如果有)。

似乎有一个 big discussion 正在改变关于 scala 语言组的逆变参数定义“特异性”的方式。

【讨论】:

  • 啊,我明白了。我希望我在 Scala 的第二天会更顺利!
【解决方案2】:

在当前的 API 中,这些方法阻止了它的逆变:

  /** 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

【讨论】:

  • 不过,通过参数化 T 的子类型来修复 maxmin 很容易。
  • 正确 -- 最具体的参数解析将是最准确的答案。
猜你喜欢
  • 1970-01-01
  • 2016-07-27
  • 1970-01-01
  • 1970-01-01
  • 2015-02-09
  • 2017-09-01
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多