【发布时间】: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 <: CodeTree] 实例定义一个隐式工厂函数。
【问题讨论】:
-
看起来这是一个与类型推断器未能找到最具体的排序有关的技术问题。详情请见scala-programming-language.1934581.n4.nabble.com/…。
-
@Impredicative 我用一个讨厌的解决方法编辑了这篇文章。
标签: scala generics types contravariance type-systems