Traversable的继承和类型参数

时间:2010-04-28 23:22:47

标签: generics scala scala-2.8 scala-collections

我正在研究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注释也与此有关吗? (这似乎是一种可能不安全的黑客,迫使事情发挥作用......)。

编辑 - 在this question中找到了关于注释的一些有用的答案(因为GenericTraversableTemplate用于具有不同方差的可变和不可变集合。)

问题:当您查看层次结构时,您会看到Traversable继承HasNewBuilder两次(一次通过TraversableLike,一次通过GenericTraversableTemplate),但类型略有不同参数。这是如何工作的?为什么不同的类型参数不会导致错误?

1 个答案:

答案 0 :(得分: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类型参数如何获取自己的参数,但该参数未由声明中的任何其他类型参数定义?这是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]是一个这样的结果类型,并且正如我们计算的那样,它是一种*(它不需要参数)。