I'm studying the source code of the Scala 2.8 collection classes. I have questions about the hierarchy of scala.collection.Traversable
. Look at the following declarations:
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]
Question: Why does Traversable
extend GenericTraversableTemplate
with type parameters [A, Traversable]
- why not [A, Traversable[A]]
? I tried some experimenting with a small program with the same structure and got a strange error message when I tried to change it to Traversable[A]
:
error: Traversable[A] takes no type parameters, expected: one
I guess that the use of the @uncheckedVariance
annotation in GenericTraversableTemplate
also has to do with this? (That seems like a kind of potentially unsafe hack to force things to work...).
edit - found some useful answers about the annotation in this question (it is because GenericTraversableTemplate
is used for both mutable and immutable collections which have different variance).
Question: When you look at the hierarchy, you see that Traversable
inherits HasNewBuilder
twice (once via TraversableLike
and once via GenericTraversableTemplate
), but with slightly different type parameters. How does this work exactly? Why don't the different type parameters cause an error?
The reason is the CC
parameter in the GenericTraversableTemplate
trait. Unlike a normal type parameter which has kind *
(pronounced "type"), this parameter has type * => *
(pronounced "type to type"). In order to understand what this means, you first need to have a little background about kinds.
Consider the following snippet:
val a: Int = 42
Here we see 42
, which is a value. Values have intrinsic types. In this case, our value is 42
and the type is Int
. A type is something like a category which encompasses many values. It says something about the values which are possible for variable a
. For example, we know that a
cannot contain the value "foobar"
, because that value has type String
. Thus, values are sort of like the first level of abstraction, while types are one level above values.
So here's the question: what's stopping us from taking this one step further? If values can have types, why can't types have "something" above them? That "something" is called a kind. Kinds are to types what types are to values, generic categories which restrict what sort of types can be described.
Let's look at some concrete examples:
type String
type Int
type List[Int]
These are types, and they all have kind *
. This is the most common kind (which is why we call it "type"). In practice, most types have this kind. However, some do not:
type List // note: compile error
Here we have the type constructor List
, but this time we "forgot" to specify its type parameter. As it turns out, this is actually a type, but one of a different kind. Specifically, * => *
. As the notation is meant to imply, this kind describes a type which takes another type of kind *
as a parameter, producing a new type of kind *
as a result. We can see this in the first example, where we passed the Int
type (which has kind *
) to the List
type constructor (which has kind * => *
), producing the type List[Int]
(which has kind *
).
Returning to GenericTraversableTemplate
, let's look again at the declaration:
trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]]
Notice how the CC
type parameter takes a parameter of its own, but that parameter is not defined by any other type parameter in the declaration? This is Scala's rather clumsy way of saying that CC
must be of kind * => *
(just like a
must be of type Int
in our earlier example). "Normal" type parameters (such as A
) are always of kind *
. By forcing CC
to be of kind * => *
, we're effectively telling the compiler that the only valid types which can be substituted for this parameter must be themselves of kind * => *
. Thus:
type GenericTraversableTemplate[String, List] // valid!
type GenericTraversableTemplate[String, List[Int]] // invalid!
Remember, List
is of kind * => *
(exactly what we need for CC
), but List[Int]
has kind *
, so the compiler rejects it.
For the record, GenericTraversableTemplate
itself has a kind, specifically: (* x (* => *)) => *
. This means that GenericTraversableTemplate
is a type which takes two types as parameters -- one of kind *
, the other of kind * => *
-- and produces a type of kind *
as a result. In our example above, GenericTraversableTemplate[String, List]
is one such result type, and as we computed, it is of kind *
(it takes no parameters).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With