2010-04-28 10 views
8

Estoy estudiando el código fuente de las clases de colección de Scala 2.8. Tengo preguntas sobre la jerarquía de scala.collection.Traversable. Mira las siguientes declaraciones:Parámetros de herencia y tipo de 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] 

Pregunta: ¿Por qué se extienden TraversableGenericTraversableTemplate con parámetros de tipo [A, Traversable] - por qué no [A, Traversable[A]]? Probé un poco de experimentación con un pequeño programa con la misma estructura y recibido un mensaje de error extraño cuando traté de cambiarlo a Traversable[A]:

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

supongo que el uso de la @uncheckedVariance anotación en GenericTraversableTemplate también tiene que ver con ¿esta? (Parece una clase de truco potencialmente inseguro para obligar a las cosas a funcionar ...).

edición - encontró algunas respuestas útiles acerca de la anotación en this question (es porque GenericTraversableTemplate se utiliza para las colecciones tanto mutables e inmutables que tienen diferente varianza).

Pregunta: Cuando nos fijamos en la jerarquía, se ve que Traversable hereda HasNewBuilder dos veces (una vía TraversableLike y una vez a través de GenericTraversableTemplate), pero con un poco diferentes parámetros de tipo. Como funciona esto exactamente? ¿Por qué los diferentes parámetros de tipo no causan un error?

Respuesta

16

El motivo es el parámetro CC en el rasgo GenericTraversableTemplate. A diferencia de un parámetro de tipo normal que tiene el tipo * (pronunciado "tipo"), este parámetro tiene el tipo * => * (pronunciado "tipo a tipo"). Para entender lo que esto significa, primero necesita tener un poco de conocimiento acerca de los tipos.

Considere el siguiente fragmento:

val a: Int = 42 

Aquí vemos 42, que es un valor . Los valores tienen tipos intrínsecos. En este caso, nuestro valor es 42 y el tipo es Int. Un tipo es algo así como una categoría que abarca muchos valores. Dice algo sobre los valores que son posibles para la variable a. Por ejemplo, sabemos que a no puede contener el valor "foobar", porque ese valor tiene el tipo String. Por lo tanto, los valores son algo así como el primer nivel de abstracción, mientras que los tipos son un nivel por encima de los valores.

Así que aquí está la pregunta: ¿qué nos impide avanzar un paso más? Si los valores pueden tener tipos, ¿por qué los tipos no pueden tener "algo" por encima de ellos? Ese "algo" se llama tipo. Los tipos son tipos de tipos de valores, categorías genéricas que restringen qué tipo de tipos se pueden describir. mirada

Vamos a algunos ejemplos concretos:

type String 
type Int 
type List[Int] 

Estos son los tipos, y todos ellos tienen * tipo. Este es el tipo más común (por eso lo llamamos "tipo"). En la práctica, la mayoría de los tipos tienen este tipo.Sin embargo, otros no:

type List  // note: compile error 

Aquí tenemos el constructor de tipo List, pero esta vez nos "olvidamos" para especificar el tipo de parámetro. Como resultado, este es realmente un tipo, pero uno de un tipo diferente. Específicamente, * => *. Como la notación implica, este tipo describe un tipo que toma otro tipo de tipo * como parámetro, produciendo un nuevo tipo de tipo * como resultado. Podemos ver esto en el primer ejemplo, donde pasamos el tipo Int (que tiene el tipo *) al constructor de tipo List (que tiene el tipo * => *), produciendo el tipo List[Int] (que tiene el tipo *).

Volviendo a GenericTraversableTemplate, vamos a ver de nuevo en la declaración:

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

Note como el parámetro CC tipo toma un parámetro de su propia, pero que el parámetro no está definido por cualquier otro tipo de parámetro en la declaración? Esta es la manera más bien torpe de Scala de decir que CC debe ser del tipo * => * (al igual que a debe ser del tipo Int en nuestro ejemplo anterior). Los parámetros de tipo "Normal" (como A) son siempre del tipo *. Al forzar que CC sea del tipo * => *, efectivamente le estamos diciendo al compilador que los únicos tipos válidos que pueden sustituirse por este parámetro deben ser ellos mismos del tipo * => *. Por lo tanto:

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

Recuerde, List es de tipo * => * (exactamente lo que necesitamos para CC), pero tiene List[Int]* tipo, por lo que el compilador lo rechaza.

Para el registro, GenericTraversableTemplate sí tiene un tipo, específicamente: (* x (* => *)) => *. Esto significa que GenericTraversableTemplate es un tipo que toma dos tipos como parámetros - uno de tipo *, el otro tipo * => * - y produce un tipo de tipo * como resultado. En nuestro ejemplo anterior, GenericTraversableTemplate[String, List] es uno de esos tipos de resultados, y como calculamos, es del tipo * (no requiere parámetros).

+0

¡Gracias por tomarse el tiempo para responder a esto tan claramente! Había escuchado sobre "tipos" antes pero realmente no entendía lo que significaban hasta ahora. – Jesper

Cuestiones relacionadas