2010-04-28 13 views
8

Ich studiere den Quellcode der Scala 2.8 Sammlung Klassen. Ich habe Fragen zur Hierarchie von scala.collection.Traversable. Schauen Sie sich die folgenden Erklärungen:Vererbung und Typ Parameter von Traversable

package scala.collection 
    trait Traversable[+A] 
     extends TraversableLike[A, Traversable[A]] 
     with GenericTraversableTemplate[A, Traversable] 

    trait TraversableLike[+A, +Repr] 
     extends HasNewBuilder[A, Repr] 
     with TraversableOnce[A] 

package scala.collection.generic 
    trait HasNewBuilder[+A, +Repr] 

    trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]] 
     extends HasNewBuilder[A, CC[A] @uncheckedVariance] 

Frage: Warum Traversable erweitern GenericTraversableTemplate mit Typ-Parameter [A, Traversable] - warum nicht [A, Traversable[A]]? Ich habe versucht, mit einem kleinen Programm mit der gleichen Struktur etwas experimentiert und bekam eine seltsame Fehlermeldung, wenn ich versuchte, es zu Traversable[A] zu ändern:

error: Traversable[A] takes no type parameters, expected: one 

ich, dass der Einsatz in GenericTraversableTemplate der @uncheckedVariance Anmerkung erraten auch zu tun hat mit Dies? (Das scheint eine Art potentiell unsicherer Hack zu sein, um Dinge zur Arbeit zu zwingen ...).

bearbeiten - fand einige nützliche Antworten über die Anmerkung in this question (beide es ist, weil GenericTraversableTemplate für wandelbar und unveränderlich Sammlungen verwendet wird, die unterschiedliche Varianz).

Frage: Wenn Sie an der Hierarchie betrachten, sehen Sie, dass Traversable inherits zweimal HasNewBuilder (einmal über TraversableLike und einmal über GenericTraversableTemplate), aber mit etwas anderen Art Parametern. Wie funktioniert das genau? Warum verursachen die verschiedenen Typparameter keinen Fehler?

Antwort

16

Der Grund ist der CC Parameter in der GenericTraversableTemplate Eigenschaft. Im Gegensatz zu einem normalen Typparameter mit der Art * (ausgesprochen "type") hat dieser Parameter den Typ * => * (ausgesprochen "type to type"). Um zu verstehen, was das bedeutet, müssen Sie zunächst ein wenig Hintergrundwissen über Arten haben.

Betrachten Sie das folgende Snippet:

val a: Int = 42 

Hier sehen wir 42, die ein Wert ist. Werte haben intrinsische Typen. In diesem Fall ist unser Wert 42 und der Typ ist Int. Ein Typ ist so etwas wie eine Kategorie, die viele Werte umfasst. Es sagt etwas über die Werte aus, die für die Variable a möglich sind. Zum Beispiel wissen wir, dass a den Wert "foobar" nicht enthalten kann, da dieser Wert den Typ String hat. Somit sind die Werte eine Art der ersten Abstraktionsebene, während die Typen eine Ebene über den Werten liegen.

Also hier ist die Frage: Was hält uns davon ab, diesen einen Schritt weiter zu gehen? Wenn Werte Typen haben können, warum können Typen nicht "etwas" über ihnen haben? Dieses "Etwas" wird Art genannt. Arten sind zu Typen, die zu Werten sind, generische Kategorien, die einschränken, welche Art von Typen kann beschrieben werden.

Blick Lassen Sie uns auf einige konkrete Beispiele:

type String 
type Int 
type List[Int] 

Dies sind Arten, und sie haben alle Art *. Dies ist die gebräuchlichste Art (weshalb wir es "Typ" nennen). In der Praxis haben die meisten Typen diese Art.Allerdings haben einige nicht:

type List  // note: compile error 

Hier haben wir den Typkonstruktor haben List, aber diesmal wir „vergessen“ zu seinem Typ-Parameter angeben. Wie sich herausstellt, handelt es sich tatsächlich um einen Typ, der jedoch von anderer Art ist. Genauer gesagt, * => *. Wie die Notation implizieren soll, beschreibt diese Art einen Typ, der einen anderen Typ von Art * als Parameter annimmt, wodurch ein neuer Typ von Art * erzeugt wird. Wir können das im ersten Beispiel sehen, wo wir den Int Typ (der Art * hat) an den List Typ Konstruktor (der Art hat * => *), den Typ List[Int] (der Art * hat).

Rückkehr nach GenericTraversableTemplate, schauen wir uns wieder auf die Erklärung:

trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]] 

Beachten Sie, wie der CC Typ-Parameter einen Parameter der eigenen nimmt, aber das Parameter wird nicht von einem anderen Typ-Parameter in der Deklaration festgelegt? Dies ist Scalas ziemlich ungeschickte Art, zu sagen, dass CC von Art * => * sein muss (genau wie a muss vom Typ Int in unserem früheren Beispiel sein). "Normal" -Typ-Parameter (wie A) sind immer von der Art *. Durch Erzwingen CC zu Art * => * sagen wir effektiv dem Compiler, dass die einzigen gültigen Typen, die für diesen Parameter ersetzt werden können, selbst von Art * => * sein müssen. Also:

type GenericTraversableTemplate[String, List]  // valid! 
type GenericTraversableTemplate[String, List[Int]] // invalid! 

Denken Sie daran, List ist von Art * => * (genau das, was wir für CC müssen), aber List[Int] hat Art *, so dass der Compiler lehnt sie ab.

Für die Aufzeichnung, GenericTraversableTemplate selbst hat eine Art, speziell: (* x (* => *)) => *. Dies bedeutet, dass GenericTraversableTemplate ein Typ ist, der zwei Typen als Parameter akzeptiert - einen der Art *, den anderen der Art * => * - und als Ergebnis einen Typ der Art * erzeugt. In unserem obigen Beispiel ist GenericTraversableTemplate[String, List] ein solcher Ergebnistyp, und wie wir berechnet haben, ist er vom Typ * (er benötigt keine Parameter).

+0

Danke, dass du dir die Zeit genommen hast, dies so klar zu beantworten! Ich hatte schon vorher von "Arten" gehört, aber bis jetzt nicht wirklich verstanden, was sie bedeuten. – Jesper