2013-04-17 6 views
7

Gibt es einen Grund, warum Scala Ordering Merkmal nicht kontravariant ist? Ein motivierendes Beispiel folgt.Scala: Bestellung Kontravarianz

Angenommen, ich möchte eine geordnete Beilage durchführen. Ich kann mit der Unterschrift

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

hier eine Funktion haben, ich habe ein Ordering die A Supertypen vom Typ akzeptiert. Ich denke, das ist nützlich, wenn Sie mit case classes zu tun haben. Zum Beispiel:

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

würde ich meine Insert-Funktion will mit Typen List[CodeTree], List[Leaf] oder List[Fork] zu arbeiten. Da Ordering jedoch nicht kontravariant ist, muss ich implizit Orderings für jeden case definieren.

Wenn ich definieren

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

alles wie erwartet funktioniert.

Gibt es einen anderen Weg, um mein Ziel zu erreichen?

EDIT:

Meine aktuelle Lösung ist Einsatz als

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

zu definieren, die gut funktionieren, wenn sie mit List[CodeTree] beschäftigen. Ich sehe auch (durch die scalaz Bibliothek inspiriert):

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) 

ich eine implizite Fabrik Funktion für Ordering[A <: CodeTree] Instanzen bin definieren.

+2

Sieht so aus, als wäre es ein technisches Problem, wenn der Typinterferenzer die spezifischste Reihenfolge nicht findet. Siehe http://scala-programming-language.1934581.n4.nabble.com/Contravariant-Ordering-T-java-util-Comparator-and-unchedVariance-td1955224.html für Details. – Impredicative

+1

@Impredicative Ich habe den Beitrag mit einem fiesen Workaround bearbeitet. –

Antwort

4

Ein bisschen mehr von einer detaillierten Antwort zog in dem Kommentar oben verlinkten aus dem Gewinde auf ‚scala-Sprache‘.

Der Grund dafür, dass Ordering nicht kontravariant ist, liegt darin, dass dies nicht mit dem in der impliziten Auflösung verwendeten Spezifitätsbegriff übereinstimmt. Implizite Auflösung versucht, den am meisten "spezifischen" Typ für einen Parameter auszuwählen, und betrachtet einen Typ als spezifischer als einen anderen, wenn es ein Subtyp davon ist. Dies ist in kovarianten Fällen sinnvoll: Ich hätte lieber eine implizite Spezifikation für meine String-Liste als eine für eine alte Liste. In kontra Fällen aber will es die falsche Sache holen:

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

Und so wird es die ‚spezifischste‘ Bestellung als, wenn verfügbar, Ordering[Any] holen.

Es scheint ein big discussion zu sein, der den Weg ändert, wie "Spezifität" in Bezug auf kontravariante Parameter auf der scala-sprachlichen Gruppe definiert wird.

+0

Ah, ich sehe das Problem. Ich hatte gehofft, dass mein zweiter Tag mit Scala reibungsloser verlaufen würde! –

2

In der aktuellen API verhindern diese Methoden es aus zu sein kontra:

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

Es ist einfach, 'max' und' min' durch Parametrieren über einen Untertyp von 'T' zu korrigieren. – Impredicative

+0

Wahr - die spezifischste Argumentauflösung wäre die genaueste Antwort. – axel22