2010-04-28 15 views
8

Studiuję kod źródłowy klas kolekcji Scala 2.8. Mam pytania dotyczące hierarchii scala.collection.Traversable. Spójrz na następujących deklaracji:Parametry dziedziczenia i typu z Traversalable

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] 

pytanie: Dlaczego Traversable przedłużenia GenericTraversableTemplate z parametrami typu [A, Traversable] - dlaczego nie [A, Traversable[A]]? Próbowałem niektóre eksperymenty z małego programu o tej samej strukturze i mam dziwny komunikat o błędzie, gdy próbowałem go zmienić na Traversable[A]:

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

myślę, że korzystanie z @uncheckedVariance adnotacji w GenericTraversableTemplate ma też do czynienia z to? (To wydaje się być rodzajem potencjalnie niebezpiecznego hacka, zmuszającego rzeczy do działania ...).

edit - znaleziono kilka użytecznych odpowiedzi na temat adnotacji w this question (to dlatego GenericTraversableTemplate służy zarówno do modyfikowalnych i niezmiennych zbiorów, które mają inny wariancji).

Pytanie: Kiedy patrzysz na hierarchii, widać, że Traversable dziedziczy HasNewBuilder dwukrotnie (raz przez TraversableLike i raz przez GenericTraversableTemplate), ale z nieco innych parametrów typu. Jak to działa dokładnie? Dlaczego różne parametry parametrów nie powodują błędu?

Odpowiedz

16

Powodem jest parametr CC w funkcji GenericTraversableTemplate. W przeciwieństwie do zwykłego parametru typu, który ma rodzaj * (wymawiane "typ"), ten parametr ma typ * => * (wymawiane "typ do typu"). Aby zrozumieć, co to oznacza, najpierw trzeba mieć trochę informacji o rodzajach.

Rozważmy następujący fragment:

val a: Int = 42 

tutaj widzimy 42, która jest wartością. Wartości mają wewnętrzne typy. W tym przypadku nasza wartość to 42, a typem jest Int. Typ jest czymś w rodzaju kategorii, która obejmuje wiele wartości. Mówi coś o wartościach, które są możliwe dla zmiennej a. Na przykład wiemy, że a nie może zawierać wartości "foobar", ponieważ ta wartość ma typ String. Tak więc wartości są podobne do pierwszego poziomu abstrakcji, podczas gdy typy mają jeden poziom powyżej wartości.

Oto pytanie: co nas powstrzymuje przed pójściem o krok dalej? Jeśli wartości mogą mieć typy, dlaczego typy nie mają "czegoś" powyżej? To "coś" nazywa się rodzaj. Rodzaje mają określać, jakie typy mają wartości, ogólne kategorie, które ograniczają rodzaje typów . wygląd

Miejmy na niektóre konkretne przykłady:

type String 
type Int 
type List[Int] 

Są to typy, a wszystkie one mają rodzaj *. Jest to najczęstszy rodzaj (dlatego nazywamy go "typem"). W praktyce większość typów ma ten rodzaj.Jednak niektórzy nie:

type List  // note: compile error 

Tutaj mamy konstruktora typu List, ale tym razem „zapomniał”, aby określić jej parametr typu. Jak się okazuje, jest to typ, ale inny rodzaj. W szczególności, * => *. Zgodnie z tym, co oznacza zapis, ten rodzaj opisuje typ, który przyjmuje jako parametr inny rodzaj rodzaju: *, co powoduje powstanie nowego rodzaju rodzaju: *. Widzimy to w pierwszym przykładzie, gdzie mijaliśmy Int typ (który ma rodzaj *) do konstruktora List typu (która ma rodzaj * => *), tworząc rodzaj List[Int] (który ma rodzaj *).

Wracając do GenericTraversableTemplate, spójrzmy jeszcze raz na oświadczenie:

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

Wskazówki jak parametr CC typ przyjmuje parametr własnej, ale że parametr nie jest zdefiniowany przez inny parametr typu w deklaracji? Jest to dość niezdarny sposób, w jaki Scala mówi, że CC musi być rodzaju * => * (tak jak a musi być typu Int w naszym wcześniejszym przykładzie). Parametry typu "normalnego" (takie jak A) są zawsze rodzaju *. Wymuszając, że CC jest rodzaju * => *, skutecznie informujemy kompilator, że jedynymi prawidłowymi typami, które można zastąpić tym parametrem, muszą być same w sobie rodzaju * => *. Zatem:

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

Pamiętaj, List jest rodzaj * => * (dokładnie to, czego potrzebujemy dla CC), ale List[Int] ma rodzaj *, więc kompilator odrzuci.

Sam w sobie, GenericTraversableTemplate sam w sobie ma swoisty, konkretnie: (* x (* => *)) => *. Oznacza to, że GenericTraversableTemplate to typ, który przyjmuje dwa typy jako parametry - jedyny w swoim rodzaju *, drugi rodzaj * => * - i w rezultacie daje rodzaj rodzaju *. W powyższym przykładzie, GenericTraversableTemplate[String, List] jest jednym z takich typów wyników, a ponieważ obliczyliśmy, jest on rodzaju * (nie ma żadnych parametrów).

+0

Dzięki za poświęcenie czasu, aby odpowiedzieć tak wyraźnie! Słyszałem wcześniej o "rodzajach", ale tak naprawdę nie rozumiałem, co oznaczają do tej pory. – Jesper

Powiązane problemy