Наследование и параметры типа Проходимых

Я изучаю исходный код классов 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), но с немного отличающимися параметрами типа. Как это работает точно? Почему параметры другого типа не вызывают ошибку?

8
задан Community 23 May 2017 в 12:16
поделиться

1 ответ

Причина в параметре 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] является одним из таких типов результатов, и, как мы вычислили, он имеет вид * (не требует параметров).

16
ответ дан 5 December 2019 в 10:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: