2015-06-05 32 views
2

根據以下示例,調用xs.toList.map(_.toBuffer)成功,但xs.toBuffer.map(_.toBuffer)失敗。但是當後者中的步驟使用中間結果執行時,它會成功。什麼導致這種不一致?toList和toBuffer之間的類型推斷不一致

scala> "ab-cd".split("-").toBuffer 
res0: scala.collection.mutable.Buffer[String] = ArrayBuffer(ab, cd) 

scala> res0.map(_.toBuffer) 
res1: scala.collection.mutable.Buffer[scala.collection.mutable.Buffer[Char]] = ArrayBuffer(ArrayBuffer(a, b), ArrayBuffer(c, d)) 

scala> "ab-cd".split("-").toBuffer.map(_.toBuffer) 
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toBuffer) 
       "ab-cd".split("-").toBuffer.map(_.toBuffer) 
              ^

scala> "ab-cd".split("-").toList.map(_.toBuffer) 
res3: List[scala.collection.mutable.Buffer[Char]] = List(ArrayBuffer(a, b), ArrayBuffer(c, d)) 

回答

5

看那toBuffertoList的定義:

def toBuffer[A1 >: A]: Buffer[A1] 
def toList: List[A] 

正如你所看到的,toBuffer是通用的,而toList不是。 造成這種差異的原因是 - 我相信 - Buffer是不變的,而List是協變的。

比方說,我們有以下類別:

class Foo 
class Bar extends Foo 

因爲List是協變的,你可以對Iterable[Bar]實例調用toList和治療結果作爲List[Foo]。 如果List在哪裏不變,這不會是這種情況。 Buffer是不變的,如果toBuffer被定義爲def toBuffer: Buffer[A]你同樣無法治療結果toBuffer (上Iterable[Bar]一個實例)作爲Buffer[Foo]一個實例(如Buffer[Bar]是沒有一分型Buffer[Foo],不像列表)。 但通過聲明toBufferdef toBuffer[A1 >: A](注意添加類型參數A1),我們回到有toBuffer返回實例的Buffer[Foo]可能性: 所有我們需要的是explcitly設置A1到富,或讓編譯器推斷它(如果toBuffer在期望Buffer[Foo]的站點被調用)。

我認爲這解釋了爲什麼toListtoBuffer定義不同的原因。 現在的問題是toBuffer是通用的,這可能會嚴重影響推論。

當你這樣做:

"ab-cd".split("-").toBuffer 

你從來沒有明確說A1String,但由於"ab-cd".split("-")有明確的類型Array[String],編譯器知道AString。它也知道A1 >: A(在toBuffer),並沒有任何進一步的限制,它將推斷A1正是A,換句話說String。 所以最後整個表達式返回一個Buffer[String]

但是這裏的事情:在scala中,類型推斷髮生在整個表達式中。 當你有像a.b.c,你可能會以爲階將推斷確切類型 爲a,然後從該推斷出一個確切的類型a.b,終於爲a.b.c。並非如此。 類型推斷是推遲到整個表達式a.b.c(參見scala規範「6.26。4局部類型推理 」,‘案例1:選擇’)

所以,回到你的問題,在表達"ab-cd".split("-").toBuffer.map(_.toBuffer),子表達式"ab-cd".split("-").toBuffer類型Buffer[String],而是 它保持類型爲像Buffer[A1] forSome A1 >: String。換句話說,A1是不固定的,我們只是進行約束A1 >: String到推理的下一個步驟。 這一步是map(_.toBuffer),其中map被定義爲map[C](f: (B) ⇒ C): Buffer[B]。這裏B其實是一樣的A1,但此時A1 目前還不完全知道,我們只知道A1 >: String。 這是我們的問題。編譯器需要知道匿名函數的確切類型(_.toBuffer)(僅僅是因爲實例化Function1[A,R]需要知道AR的確切類型,就像任何泛型類型一樣)。 所以你需要以某種方式明確地告訴他,因爲它無法完全推斷出來。

這意味着你需要做的要麼:

"ab-cd".split("-").toBuffer[String].map(_.toBuffer) 

或者:

"ab-cd".split("-").toBuffer.map((_:String).toBuffer) 
+0

我只是重新閱讀這個答案,非常有幫助,謝謝。 –