2013-04-17 112 views
7

斯卡拉的Ordering特质没有任何原因是不会逆转的吗?一个激励的例子如下。斯卡拉:排序反转

假设我想执行一个有序插入。我可以具有功能与签名

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

在这里,我有接受超级类型A类型的Ordering。我想这在你处理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]情况下,一个隐含的工厂函数。

+2

看起来像是一个与类型推理未能找到最具体排序有关的技术问题。有关详细信息,请参阅http://scala-programming-language.1934581.n4.nabble.com/Contravariant-Ordering-T-java-util-Comparator-and-uncheckedVariance-td1955224.html。 – Impredicative

+1

@Impredicative我用一个讨厌的解决方法编辑帖子。 –

回答

4

更详细的解答的位拉出的线对上述评论链接“的Scala语言”。

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

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

因此,这将挑选“最具体的”排序为,如果有的话,Ordering[Any]

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

+0

啊,我看到了问题。我希望我和斯卡拉的第二天更顺利! –

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 
+0

尽管通过在“T”的子类型上进行参数化来修复'max'和'min'是很容易的。 – Impredicative

+0

正确 - 最具体的参数分辨率将是最准确的答案。 – axel22