Причина - параметр CC
в признаке GenericTraversableTemplate
. В отличие от обычного параметра типа, который имеет вид *
(произносится как «тип»), этот параметр имеет тип * => *
(произносится как «тип-тип»). Чтобы понять, что это значит, сначала нужно немного рассказать о видах.
Рассмотрим следующий фрагмент:
val a: Int = 42
Здесь мы видим 42
, что является значением . Значения имеют внутренние типы. В этом случае наше значение равно 42
, а тип - Int
. Тип - это что-то вроде категории, которая включает в себя множество значений. Это говорит кое-что о значениях, которые возможны для переменной a
. Например, мы знаем, что a
не может содержать значение "foobar"
, потому что это значение имеет тип String
. Таким образом, значения подобны первому уровню абстракции, а типы на один уровень выше значений.
Итак, вот вопрос: что мешает нам сделать этот шаг вперед? Если значения могут иметь типы, почему типы не могут иметь «что-то» над ними? Это «что-то» называется kind . Виды относятся к типам, а типы - к значениям, могут быть описаны общие категории, которые ограничивают тип типов .
Давайте рассмотрим несколько конкретных примеров:
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
принимает собственный параметр, но этот параметр не определен никаким другим параметром типа в объявлении? Это довольно неуклюжий способ сказать, что 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]
является одним из таких типов результатов, и, как мы вычислили, он имеет вид *
(он не принимает параметров).