Я изучаю исходный код классов Scala 2.8 набора. У меня есть вопросы об иерархии scala.collection.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]
Вопрос: Почему делает Traversable
расшириться GenericTraversableTemplate
с параметрами типа [A, Traversable]
- почему нет [A, Traversable[A]]
? Я попробовал некоторое экспериментирование с небольшой программой с той же структурой и получил странное сообщение об ошибке, когда я пытался изменить его на Traversable[A]
:
error: Traversable[A] takes no type parameters, expected: one
Я предполагаю что использование @uncheckedVariance
аннотация в GenericTraversableTemplate
также имеет отношение к этому? (Который походит на своего рода потенциально небезопасный взлом для принуждения вещей работать...).
редактирование - нашло некоторые полезные ответы об аннотации в этом вопросе (это потому что GenericTraversableTemplate
используется и для изменяемых и для неизменных наборов, которые имеют другое различие).
Вопрос: при рассмотрении иерархии Вы видите это Traversable
наследовался HasNewBuilder
дважды (однажды через TraversableLike
и однажды через GenericTraversableTemplate
), но с немного отличающимися параметрами типа. Как это работает точно? Почему параметры другого типа не вызывают ошибку?
Причина в параметре CC
в характеристике GenericTraversableTemplate
. В отличие от параметра обычного типа, который имеет вид *
(произносится как «тип»), этот параметр имеет тип * => *
(произносится как «тип для типа»). Чтобы понять, что это значит, сначала нужно немного узнать о видах.
Рассмотрим следующий фрагмент:
val a: Int = 42
Здесь мы видим 42
, что является значением . У значений есть внутренние типы. В данном случае наше значение 42
, а тип - Int
. Тип - это что-то вроде категории, которая включает в себя множество значений. В нем кое-что говорится о значениях, которые возможны для переменной a
. Например, мы знаем, что a
не может содержать значение «foobar»
, потому что это значение имеет тип String
. Таким образом, значения подобны первому уровню абстракции, а типы - на один уровень выше значений.
Итак, вот вопрос: что мешает нам сделать еще один шаг вперед? Если значения могут иметь типы, почему типы не могут иметь «что-то» над собой? Это «нечто» называется видом . Виды относятся к типам, а типы относятся к значениям, общие категории, которые ограничивают то, какие типы типов могут быть описаны.
Давайте рассмотрим несколько конкретных примеров:
type String
type Int
type List[Int]
Это типы, и все они имеют вид *
. Это самый распространенный вид (поэтому мы называем его «типом»). На практике такой вид есть у большинства типов.Однако некоторые этого не делают:
type List // note: compile error
Здесь у нас есть конструктор типа List
, но на этот раз мы «забыли» указать его параметр типа. Оказывается, это действительно тип, но совсем другого типа. В частности, * => *
. Как следует из обозначения, этот тип описывает тип, который принимает другой тип вида *
в качестве параметра, создавая в результате новый тип вида *
. Мы можем видеть это в первом примере, где мы передали тип Int
(имеющий вид *
) в конструктор типа List
(имеющий вид ] * => *
), создавая тип List [Int]
(имеющий вид *
).
Возвращаясь к GenericTraversableTemplate
, давайте еще раз посмотрим на объявление:
trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]]
Обратите внимание, что параметр типа CC
принимает собственный параметр, но этот параметр не определяется никакими другой параметр типа в объявлении? Это довольно неуклюжий способ Scala сказать, что CC
должен быть типа * => *
(точно так же, как a
должен иметь тип Int
] в нашем предыдущем примере). Параметры «нормального» типа (такие как A
) всегда имеют вид *
. Принудительно заставляя CC
иметь вид * => *
, мы фактически сообщаем компилятору, что единственными допустимыми типами, которые могут быть заменены для этого параметра, должны быть сами типы ] * => *
.Таким образом:
type GenericTraversableTemplate[String, List] // valid!
type GenericTraversableTemplate[String, List[Int]] // invalid!
Помните, List
имеет вид * => *
(именно то, что нам нужно для CC
), но List [Int]
имеет вид *
, поэтому компилятор отклоняет его.
Для записи, GenericTraversableTemplate
сам по себе имеет вид, а именно: (* x (* => *)) => *
. Это означает, что GenericTraversableTemplate
- это тип, который принимает в качестве параметров два типа - один вида *
, другой типа * => *
- и производит тип вида *
в результате. В нашем примере выше GenericTraversableTemplate [String, List]
является одним из таких типов результатов, и, как мы вычислили, он имеет вид *
(не требует параметров).