【问题标题】:Inheritance and type parameters of TraversableTraversable的继承和类型参数
【发布时间】:2011-02-13 14:18:06
【问题描述】:

我正在研究 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 使用类型参数[A, Traversable] 扩展GenericTraversableTemplate - 为什么不是[A, Traversable[A]]?我尝试了一些具有相同结构的小程序并尝试将其更改为Traversable[A]时得到了一个奇怪的错误消息:

error: Traversable[A] takes no type parameters, expected: one

我猜GenericTraversableTemplate@uncheckedVariance注解的使用也和这个有关吧? (这似乎是一种潜在的不安全黑客来强制工作......)。

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

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

【问题讨论】:

    标签: generics scala scala-2.8 scala-collections


    【解决方案1】:

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

    考虑以下 sn-p:

    val a: Int = 42
    

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

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

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

    type String
    type Int
    type List[Int]
    

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

    type List     // note: compile error
    

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

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

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

    注意CC 类型参数如何获取它自己的参数,但该参数不是由声明中的任何其他类型参数定义的?这是 Scala 相当笨拙的说法,即 CC 必须是 * =&gt; * 类型(就像在我们前面的示例中 a 必须是 Int 类型一样)。 “普通”类型参数(例如A)总是类型为*。通过强制CC* =&gt; * 相同,我们实际上是在告诉编译器,唯一可以替换此参数的有效类型必须是它们自己的那种* =&gt; *。因此:

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

    请记住,List 是一种类型 * =&gt; *(正是我们所需要的 CC),但 List[Int] 是一种类型 *,因此编译器会拒绝它。

    为了记录,GenericTraversableTemplate 本身也有一种,具体来说:(* x (* =&gt; *)) =&gt; *。这意味着GenericTraversableTemplate 是一个接受两种类型作为参数的类型——一种是*,另一种是* =&gt; *——并因此产生一种类型*。在我们上面的示例中,GenericTraversableTemplate[String, List] 就是这样一种结果类型,并且根据我们的计算,它是一种 *(它不带参数)。

    【讨论】:

    • 感谢您花时间这么清楚地回答这个问题!我以前听说过“种类”,但直到现在才真正理解它们的含义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    相关资源
    最近更新 更多