Traversable 的继承和类型参数

发布于 2024-08-30 20:55:07 字数 1496 浏览 4 评论 0原文

我正在研究 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 ,可遍历[A]]?我尝试了一些具有相同结构的小程序的实验,当我尝试将其更改为 Traversable[A] 时,收到了一条奇怪的错误消息:

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

我猜想使用 @uncheckedVariance< GenericTraversableTemplate 中的 /code> 注释也与此有关吗? (这似乎是一种潜在不安全的黑客手段,迫使事情正常运转......)。

编辑 - 在 这个问题(这是因为 GenericTraversableTemplate 用于具有不同方差的可变和不可变集合)。

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

I'm studying the source code of the Scala 2.8 collection classes. I have questions about the hierarchy of scala.collection.Traversable. Look at the following declarations:

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]

Question: Why does Traversable extend GenericTraversableTemplate with type parameters [A, Traversable] - why not [A, Traversable[A]]? I tried some experimenting with a small program with the same structure and got a strange error message when I tried to change it to Traversable[A]:

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

I guess that the use of the @uncheckedVariance annotation in GenericTraversableTemplate also has to do with this? (That seems like a kind of potentially unsafe hack to force things to work...).

edit - found some useful answers about the annotation in this question (it is because GenericTraversableTemplate is used for both mutable and immutable collections which have different variance).

Question: When you look at the hierarchy, you see that Traversable inherits HasNewBuilder twice (once via TraversableLike and once via GenericTraversableTemplate), but with slightly different type parameters. How does this work exactly? Why don't the different type parameters cause an error?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心头的小情儿 2024-09-06 20:55:07

原因是 GenericTraversableTemplate 特征中的 CC 参数。与具有类型 * (发音为“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] 就是这样一种结果类型,正如我们计算的那样,它属于 * 类型(它不带任何参数)。

The reason is the CC parameter in the GenericTraversableTemplate trait. Unlike a normal type parameter which has kind * (pronounced "type"), this parameter has type * => * (pronounced "type to type"). In order to understand what this means, you first need to have a little background about kinds.

Consider the following snippet:

val a: Int = 42

Here we see 42, which is a value. Values have intrinsic types. In this case, our value is 42 and the type is Int. A type is something like a category which encompasses many values. It says something about the values which are possible for variable a. For example, we know that a cannot contain the value "foobar", because that value has type String. Thus, values are sort of like the first level of abstraction, while types are one level above values.

So here's the question: what's stopping us from taking this one step further? If values can have types, why can't types have "something" above them? That "something" is called a kind. Kinds are to types what types are to values, generic categories which restrict what sort of types can be described.

Let's look at some concrete examples:

type String
type Int
type List[Int]

These are types, and they all have kind *. This is the most common kind (which is why we call it "type"). In practice, most types have this kind. However, some do not:

type List     // note: compile error

Here we have the type constructor List, but this time we "forgot" to specify its type parameter. As it turns out, this is actually a type, but one of a different kind. Specifically, * => *. As the notation is meant to imply, this kind describes a type which takes another type of kind * as a parameter, producing a new type of kind * as a result. We can see this in the first example, where we passed the Int type (which has kind *) to the List type constructor (which has kind * => *), producing the type List[Int] (which has kind *).

Returning to GenericTraversableTemplate, let's look again at the declaration:

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

Notice how the CC type parameter takes a parameter of its own, but that parameter is not defined by any other type parameter in the declaration? This is Scala's rather clumsy way of saying that CC must be of kind * => * (just like a must be of type Int in our earlier example). "Normal" type parameters (such as A) are always of kind *. By forcing CC to be of kind * => *, we're effectively telling the compiler that the only valid types which can be substituted for this parameter must be themselves of kind * => *. Thus:

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

Remember, List is of kind * => * (exactly what we need for CC), but List[Int] has kind *, so the compiler rejects it.

For the record, GenericTraversableTemplate itself has a kind, specifically: (* x (* => *)) => *. This means that GenericTraversableTemplate is a type which takes two types as parameters -- one of kind *, the other of kind * => * -- and produces a type of kind * as a result. In our example above, GenericTraversableTemplate[String, List] is one such result type, and as we computed, it is of kind * (it takes no parameters).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文