8

我正在研究 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了两次(一次 viaTraversableLike和一次 via GenericTraversableTemplate),但类型参数略有不同。这究竟是如何工作的?为什么不同的类型参数不会导致错误?

4

1 回答 1

17

原因是特征中的CC参数GenericTraversableTemplate。与具有种类*(发音为“type”)的普通类型参数不同,此参数具有类型* => *(发音为“type to type”)。为了理解这意味着什么,您首先需要了解有关种类的一些背景知识。

考虑以下代码段:

val a: Int = 42

在这里我们看到42,这是一个。值具有内在类型。在这种情况下,我们的值是42,类型是Int。类型类似于包含许多值的类别。它说明了 variable 可能的值a。例如,我们知道a不能包含 value "foobar",因为该 value 具有 type String。因此,值有点像第一级抽象,而类型比值高一级。

那么问题来了:是什么阻止我们更进一步?如果值可以有类型,为什么类型不能在它们上面有“东西”?那个“东西”被称为kind。种类之于类型什么类型之于值,通用类别限制了可以描述的类型。

让我们看一些具体的例子:

type String
type Int
type List[Int]

这些是类型,它们都有 kind *。这是最常见的类型(这就是我们称之为“类型”的原因)。在实践中,大多数类型都有这种类型。但是,有些人不会:

type List     // note: compile error

这里我们有类型构造函数List,但这次我们“忘记”指定它的类型参数。事实证明,这实际上是一种类型,但却是另一种类型。具体来说,* => *。正如符号所暗示的那样,这种类型描述了一种以另一种类型*作为参数的类型,从而产生一种新类型的类型*。我们可以在第一个示例中看到这一点,我们将Int类型(具有 kind *)传递给List类型构造函数(具有 kind * => *),生成类型List[Int](具有 kind *)。

回到GenericTraversableTemplate,让我们再看一下声明:

trait GenericTraversableTemplate[+A, +CC[X] <: Traversable[X]]

请注意CC类型参数如何采用自己的参数,但该参数未由声明中的任何其他类型参数定义?这是 Scala 的一种相当笨拙的说法,即CCmust be of kind * => *(就像我们前面示例中的amust be of type一样)。Int“普通”类型参数(例如A)总是 kind *。通过强制CC为 kind * => *,我们有效地告诉编译器,唯一可以替换此参数的有效类型必须是它们自己的 kind * => *。因此:

type GenericTraversableTemplate[String, List]        // valid!
type GenericTraversableTemplate[String, List[Int]]   // invalid!

请记住,Listis of kind * => *(正是我们需要的CC),但是List[Int]有 kind *,所以编译器拒绝它。

记录一下,GenericTraversableTemplate本身就有一种,具体来说:(* x (* => *)) => *. 这意味着这GenericTraversableTemplate是一种将两种类型作为参数的类型——一种是 kind *,另一种是 kind——* => *并产生一种 kind*作为结果。在我们上面的示例中,GenericTraversableTemplate[String, List]是一种这样的结果类型,并且正如我们计算的那样,它是一种类型*(它不带参数)。

于 2010-04-29T04:57:25.223 回答