2017-01-02 35 views
1

考慮以下REPL會話未發現:CanBuildFrom時隱式地調用

@ def test[C[X] <: TraversableOnce[X]](implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = cbf() 
defined function test 

@ test[List] 
res32: collection.mutable.Builder[Int, List[Int]] = ListBuffer() 

@ def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
cmd33.sc:1: Cannot construct a collection of type C[Int] with elements of type Int based on a collection of type C[Int]. 
def test[C[X] <: TraversableOnce[X]] = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
               ^
Compilation Failed 

test函數的第一個定義編譯和作品,而第二個不能編譯。它們之間的唯一區別就是如何獲得CanBuildFrom的實例。在第一種情況下,它被聲明爲隱式參數,要求編譯器找到一個參數。在第二種情況下,它通過implicitly函數調用,理論上,它應該在隱式搜索範圍方面表現相同。什麼導致這種行爲?

回答

1

implicitly(在Predef)的定義是:

def implicitly[A](implicit ev: A): A = A 

它只是使內容簡潔明瞭,你(在使用現場)範圍一個隱含已

現在,當你這樣寫:

import collection.generic.CanBuildFrom 

def test[C[X] <: TraversableOnce[X]] 
    (implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = ??? 

你問來電者(在調用點)提供隱式。

當你寫

def test[C[X] <: TraversableOnce[X]] = 
    implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 

你問的編譯器與呼叫implicitly來查找一個隱含已經在範圍但是你沒有任何隱含的範圍內的給定類型!所以test的兩個定義正在完全不同。

您通常使用implicitly來獲取您沒有名稱的隱式,因爲它是使用上下文邊界或類型表示法指定的,如def test[A: TypeClass]。你不能在這裏使用這個符號,因爲CanBuildFrom有三個類型參數,而不是一個。所以你不能在這裏用implicitly做很多事情。

你可以使用implicitly與第一種情況:

def test[C[X] <: TraversableOnce[X]] 
    (implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = { 

    implicit val onceAgain = implicitly[CanBuildFrom[C[Int], Int, C[Int]]] 
    assert(onceAgain == cbf) 
} 

但你已經知道你有一個隱含的名稱爲cbf ...


注意,您可以弄個已知集合類型的隱含CanBuildFrom

implicitly[CanBuildFrom[List[Int], Int, List[Int]]] // works! 

但是,如果您的收集類型(C[X])是抽象的,則這不起作用。

+0

但這兩種情況下的隱式作用域有什麼區別?正如我所說的,我在REPL中這樣做了,所以在第一種情況下,當我調用該函數時,找到了一個通用的'CanBuildFrom'(在Predef中,我想)。這意味着,範圍中有一個適當的'CanBuildFrom'隱式實例。爲什麼在REPL中定義獨立函數時不能使用同一個函數? – Haspemulator

+0

在一種情況下,你有一個具體的類型,例如'List',在另一箇中有一個抽象類型參數'C [_]'。隱式解析將在類型類的同伴對象('CanBuildFrom')及其類型參數(例如'List' - 它找到類型類實例!)中尋找其他類型,但顯然無法解析抽象類型。 –

+0

對,現在它是有道理的。 – Haspemulator

相關問題