[英]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.