2013-04-17 37 views
7

Czy jest jakikolwiek powód, dla którego cecha Scali Ordering nie jest sprzeczna? Następujący motywacyjny przykład.Scala: Zamawianie kontrawariancji

Załóżmy, że chcę wykonać zamówioną wkładkę. I może mieć funkcję z podpisem

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

Tutaj Mam Ordering który akceptuje Super typy typu A. Wyobrażam sobie, że jest to przydatne, gdy masz do czynienia z case classes. Na przykład:

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

chciałbym moja funkcja insert do pracy z typami List[CodeTree], List[Leaf] lub List[Fork]. Jednak, ponieważ Ordering nie jest sprzeczny, muszę zdefiniować domyślne Orderings dla każdego case.

Gdybym określić

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

wszystko działa zgodnie z oczekiwaniami.

Czy istnieje inny sposób osiągnięcia mojego celu?

EDIT:

Moje obecne rozwiązanie jest zdefiniowanie wkładkę jak

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

który działa dobrze, gdy do czynienia z List[CodeTree]. I również zdefiniować (inspirowane przez bibliotekę 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) 

jestem definiowania niejawny funkcji fabrycznego dla Ordering[A <: CodeTree] przypadkach.

+2

Wygląda na to, że jest to problem techniczny polegający na tym, że inferencja typu nie jest w stanie znaleźć najbardziej konkretnego zamówienia. Więcej informacji można znaleźć na stronie http://scala-programming-language.1934581.n4.nabble.com/Contravariant-Ordering-T-java-util-Comparator-and-uncheckedVariance-td1955224.html. – Impredicative

+1

@Impredicative Edytowałem post z przykrym obejściem. –

Odpowiedz

4

Nieco bardziej szczegółowej odpowiedzi wyciągnął nitki na „scala-język” połączonej w komentarzu powyżej.

Powód, dla którego Ordering nie jest sprzeczny z wersją, polega na tym, że nie ma to rozsądnego sensu w stosunku do pojęcia specyficzności zastosowanego w domyślnej rozdzielczości. Niejawna rozdzielczość będzie próbowała wybrać najbardziej "specyficzny" typ parametru i uważa jeden typ za bardziej szczegółowy niż inny, jeśli jest to jego podtyp. Ma to sens w przypadkach kowariancyjnych: wolałbym mieć ukrytą specyfikację dla mojej listy ciągów niż jedną dla jakiejkolwiek starej listy. W kontrawariantny przypadkach jednak, że chce odebrać źle:

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

A więc będzie wybrać najbardziej specyficzny „” zamawiania jako, jeśli są dostępne, Ordering[Any].

Wygląda na to, że big discussion zmienia sposób zdefiniowania "specyficzności" w odniesieniu do parametrów przeciwwariantowych w grupie języków scala.

+0

Ah, widzę problem. Miałem nadzieję, że mój drugi dzień z Scalą pójdzie gładko! –

2

W bieżącym API, metody te zapobiec jej kontrawariantny:

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

Łatwo jest jednak naprawić 'max' i' min' przez parametrowanie względem podtypu 'T'. – Impredicative

+0

Prawda - najbardziej dokładna odpowiedź argumentu byłaby najbardziej dokładna. – axel22