gpt4 book ai didi

generics - Traversable 的继承和类型参数

转载 作者:行者123 更新时间:2023-12-02 18:02:22 26 4
gpt4 key购买 nike

我正在研究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 注释也与此有关? (这似乎是一种潜在不安全的黑客手段,迫使事情正常运转......)。

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

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

最佳答案

原因是 GenericTraversableTemplate 特征中的 CC 参数。与具有类型 * (发音为“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 类型构造函数(其类型为 *) kind * => *),生成类型 List[Int](具有 kind *)。

回到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] 就是这样一种结果类型,正如我们计算的那样,它属于 * 类型(它不带任何参数)。

关于generics - Traversable 的继承和类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2733780/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com