Traversable 的继承和类型参数
我正在研究 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
原因是
GenericTraversableTemplate
特征中的CC
参数。与具有类型*
(发音为“type”)的普通类型参数不同,此参数具有类型* => *
(发音为“键入到键入”)。为了理解这意味着什么,您首先需要了解一些有关类型的背景知识。考虑以下代码片段:
这里我们看到
42
,它是一个值。值具有内在类型。在本例中,我们的值为42
,类型为Int
。类型类似于包含许多值的类别。它说明了变量a
可能的值。例如,我们知道a
不能包含值"foobar"
,因为该值的类型为String
。因此,值有点像第一级抽象,而类型则比值高一级。那么问题来了:是什么阻止我们更进一步呢?如果值可以有类型,为什么类型上面不能有“东西”?这个“东西”被称为种类。种类之于类型就像类型之于值一样,通用类别限制了可以描述的类型。
让我们看一些具体的例子:
这些都是类型,它们都有类型
*
。这是最常见的类型(这就是我们称之为“类型”的原因)。实际上,大多数类型都有这种类型。然而,有些则不然:这里我们有类型构造函数
List
,但是这次我们“忘记”指定它的类型参数。事实证明,这实际上是一种类型,但却是另一种类型。具体来说,* => *。正如该符号所暗示的那样,这种类型描述了一种类型,该类型采用另一种类型
*
作为参数,从而生成新类型的类型*
作为结果。我们可以在第一个示例中看到这一点,其中我们将Int
类型(其类型为*
)传递给List
类型构造函数(其类型为*
) kind* => *
),生成类型List[Int]
(其具有 kind*
)。返回到 GenericTraversableTemplate,让我们再次看一下声明:
注意 CC 类型参数如何采用自己的参数,但该参数没有由任何其他类型参数定义声明?这是 Scala 相当笨拙的方式来表示
CC
必须是一种* =>; *
(就像前面示例中的a
必须是Int
类型一样)。 “普通”类型参数(例如A
)始终为*
类型。通过强制CC
为同类* => *
,我们有效地告诉编译器,可以替换此参数的唯一有效类型必须是其类型* =>; *。因此:
记住,
List
是一种* =>; *
(正是我们CC
所需要的),但是List[Int]
具有类型*
,因此编译器会拒绝它。根据记录,
GenericTraversableTemplate
本身有一种类型,具体来说:(* x (* => *)) => *。这意味着
GenericTraversableTemplate
是一种采用两种类型作为参数的类型 - 一种是*
类型,另一种是* =>; *
—— 并产生一种*
类型作为结果。在上面的示例中,GenericTraversableTemplate[String, List]
就是这样一种结果类型,正如我们计算的那样,它属于*
类型(它不带任何参数)。The reason is the
CC
parameter in theGenericTraversableTemplate
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:
Here we see
42
, which is a value. Values have intrinsic types. In this case, our value is42
and the type isInt
. A type is something like a category which encompasses many values. It says something about the values which are possible for variablea
. For example, we know thata
cannot contain the value"foobar"
, because that value has typeString
. 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:
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: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 theInt
type (which has kind*
) to theList
type constructor (which has kind* => *
), producing the typeList[Int]
(which has kind*
).Returning to
GenericTraversableTemplate
, let's look again at the declaration: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 thatCC
must be of kind* => *
(just likea
must be of typeInt
in our earlier example). "Normal" type parameters (such asA
) are always of kind*
. By forcingCC
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:Remember,
List
is of kind* => *
(exactly what we need forCC
), butList[Int]
has kind*
, so the compiler rejects it.For the record,
GenericTraversableTemplate
itself has a kind, specifically:(* x (* => *)) => *
. This means thatGenericTraversableTemplate
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).