2009-03-24 12 views
83

EDIT: Um diese Frage neu geschrieben, basierend auf Original AntwortWarum ist Scala's unveränderliches Set in seinem Typ nicht kovariant?

Die scala.collection.immutable.Set Klasse kovariant in seiner Art Parametern nicht. Warum ist das?

import scala.collection.immutable._ 

def foo(s: Set[CharSequence]): Unit = { 
    println(s) 
} 

def bar(): Unit = { 
    val s: Set[String] = Set("Hello", "World"); 
    foo(s); //DOES NOT COMPILE, regardless of whether type is declared 
      //explicitly in the val s declaration 
} 
+0

Es lohnt sich zu konstruieren, unter Hinweis darauf, dass 'foo (s.toSet [CharSequence])' kompiliert in Ordnung. Die 'toSet'-Methode ist O (1) - es wird nur' asInstanceOf' umbrochen. –

+1

Beachten Sie auch, dass 'foo (Set (" Hello "," World "))' auch auf 2.10 kompiliert, da Scala in der Lage ist, den richtigen Typ von Set abzuleiten. Es funktioniert jedoch nicht mit impliziten Konvertierungen (http://stackoverflow.com/questions/23274033/implicit-definition-working-for-seq-but-not-for-set/). –

Antwort

48

Set ist in seinem Typparameter wegen des Konzepts hinter Sätzen als Funktionen invariant. Die folgenden Signaturen sollten die Dinge klären leicht:

trait Set[A] extends (A=>Boolean) { 
    def apply(e: A): Boolean 
} 

Wenn Set in A kovariant wurden, die apply Verfahren nicht in der Lage wäre, einen Parameter des Typs nehmen A aufgrund der Kontra von Funktionen. Set könnte möglicherweise kontra in A sein, aber auch dies verursacht Probleme, wenn Sie solche Dinge zu tun:

def elements: Iterable[A] 

Kurz gesagt, die beste Lösung ist, die Dinge unveränderlich, auch für die unveränderliche Datenstruktur zu halten. Sie werden feststellen, dass immutable.Map in einem seiner Typparameter auch invariant ist.

+4

Ich denke, dass dieses Argument um "das Konzept hinter Sets als Funktionen" geht - könnte dies erweitert werden? Welche Vorteile hat zum Beispiel "ein Set als Funktion"? Geben Sie mir, dass ein "Set als eine Sammlung" nicht? Lohnt es sich die Verwendung dieser kovariante Art zu verlieren? –

+0

Angesichts Ihrer Antwort würden Sie (im Allgemeinen) * nicht * Set als den Typ von einer API zurückgegeben?Ich würde häufig eine Service-Implementierung haben eine interne Set in Java, mit der Service-API selbst wickeln diese in einer Sammlung. nicht änderbar auf eine Zugriffsmethode. –

+0

Ich denke, ich werde eine verwandte Frage stellen –

5

EDIT: für fragen jemand, warum diese Antwort ein wenig vom Thema scheint, ist dies, weil ich (der Fragesteller) die Frage geändert haben.

Scala-Typ-Inferenz ist gut genug, um herauszufinden, dass Sie in einigen Situationen CharSequences und nicht Strings wollen. Insbesondere für mich die folgenden Arbeiten in 2.7.3:

import scala.collections.immutable._ 
def findCharSequences(): Set[CharSequence] = Set("Hello", "World") 

Was wie immutable.HashSets direkt zu erstellen: dies nicht tun. Als Implementierungsoptimierung sind unveränderliche Hash-Sets von weniger als 5 Elementen keine Instanzen von immutable.HashSet. Sie sind entweder EmptySet, Set1, Set2, Set3 oder Set4. Diese Klassen Unterklasse unveränderlich. Set, aber nicht unveränderlich. HashSet.

+0

Sie haben Recht; in dem Versuch, mein aktuelles Beispiel zu vereinfachen, habe ich einen trivialen Fehler gemacht :-( –

46

bei http://www.scala-lang.org/node/9764 Martin Odersky schreibt:

„In der Frage der Sätze, ich glaube, dass die Nicht-Varianz ergibt sich auch aus den Implementierungen gemeinsame Sätze sind implementiert als Hash-Tabellen, die nicht-Variante Arrays von der sind. Schlüsseltyp. Ich stimme zu, es ist eine leicht lästige Unregelmäßigkeit. "

So scheint es, dass alle unsere Bemühungen, einen prinzipien Grund dafür waren fehlgeleitet :-)

+1

Aber einige Sequenzen sind auch mit Arrays implementiert, und immer noch 'Seq' ist kovariant ... vermisse ich etwas? –

+2

Dies könnte trivial gelöst werden, indem 'Array [Any]' intern gespeichert wird. – rightfold

+0

@rightfold ist korrekt. Es mag einen vernünftigen Grund geben, aber das ist es nicht. –