[英]Type Mismatch on Recursive Call of Method with Generic Traversable Parameters in Scala
[英]Inheritance and type parameters of 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
使用类型参数扩展GenericTraversableTemplate
[A, Traversable]
- 为什么不[A, Traversable[A]]
? 我尝试了一些具有相同结构的小程序进行实验,并在我尝试将其更改为Traversable[A]
时收到一条奇怪的错误消息:
error: Traversable[A] takes no type parameters, expected: one
我想在GenericTraversableTemplate
使用@uncheckedVariance
注释也与此有关吗? (这似乎是一种可能不安全的黑客,迫使事情发挥作用......)。
编辑 - 在这个问题中找到关于注释的一些有用的答案(这是因为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
类型构造函数(有类型* => *
),生成类型List[Int]
(有类型*
)。
回到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]
就是这样一种结果类型,正如我们计算的那样,它是一种类型*
(它不需要参数)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.