2013-04-17 107 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