Scala:排序逆变

标签 scala generics types contravariance type-systems

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

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

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

在这里,我有一个 Ordering接受类型 A 的父类(super class)型.我想这在您处理 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不是逆变的,我需要定义隐式 Orderings每个case .

如果我定义
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 <: CodeTree] 定义一个隐式工厂函数实例。

最佳答案

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

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

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

因此它会选择“最具体”的顺序,如果有的话,Ordering[Any] .

看起来有一个 big discussion继续改变关于 Scala 语言组的逆变参数定义“特异性”的方式。

关于Scala:排序逆变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16057973/

相关文章:

scala - 在 Play 2.4 scala 中禁用单个测试

scala - 为什么scala不能推断偏应用中省略参数的类型?

scala - 在 Scala 中使用运算符 orElse 的正确方法是什么?

java - 结合原始类型和泛型方法

c# - .NET - 对于非常大的十进制数使用什么数据类型?

haskell - Monad 理论和 Haskell

java - 使用什么框架来引导我的第一个生产 Scala 项目?

oracle - 重构 PL/SQL : many cursors with same rowtype (in theory)

Java 泛型 - 使用上限和下限通配符进行迭代

C# 如何将函数调用保存在内存中供以后调用