2010-04-28 2 views
8

Я изучаю исходный код классов коллекции Scala 2.8. У меня есть вопросы об иерархии scala.collection.Traversable. Посмотрите на следующие заявления:Наследование и параметры типа 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 также имеет отношение к это? (Это кажется своего рода потенциально опасным взломом, чтобы заставить работать ...).

редактировать - нашел некоторые полезные ответы о аннотации в this question (это потому, что GenericTraversableTemplate используется как для изменяемых и неизменяемых коллекций, имеющих различную дисперсия).

Вопрос: Когда вы смотрите на иерархии, вы видите, что Traversable наследуется HasNewBuilder дважды (один раз через TraversableLike и один раз через GenericTraversableTemplate), но с несколько другими параметрами типа. Как это работает? Почему разные параметры типа не вызывают ошибку?

ответ

16

Причиной является параметр CC в признаке GenericTraversableTemplate. В отличие от параметра нормального типа, который имеет вид * (произносится как «type»), этот параметр имеет тип * => * (произносится как «type to type»). Чтобы понять, что это значит, сначала вам нужно немного узнать о видах.

Рассмотрим следующий фрагмент кода:

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 типа принимает параметр самостоятельно, но этот параметр не определен каким-либо другой параметр типа в объявлении? Это довольно неуклюжий способ Скалы сказать, что 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] является одним из таких типов результатов, и, как мы вычислили, он имеет вид * (он не принимает параметров).

+0

Спасибо, что нашли время, чтобы ответить на это так ясно! Раньше я слышал о «видах», но на самом деле не понимал, что они означают до сих пор. – Jesper

 Смежные вопросы

  • Нет связанных вопросов^_^