Reputation: 11318
Consider the following REPL session:
@ 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
The first definition of test
function compiles and works, while the second one doesn't compile. The only difference between them is the way how the instance of CanBuildFrom
is obtained. In first case it's declared as implicit parameter, requiring the compiler to find one. In second case it's invoked via implicitly
function, which, in theory, should behave the same in terms of implicit search scope. What causes this behavior?
Upvotes: 1
Views: 1038
Reputation: 67330
The definition of implicitly
(in Predef
) is:
def implicitly[A](implicit ev: A): A = A
It simply makes explicit to you an implicit already in scope (at the use site).
Now when you write this:
import collection.generic.CanBuildFrom
def test[C[X] <: TraversableOnce[X]]
(implicit cbf: CanBuildFrom[C[Int], Int, C[Int]]) = ???
You are asking the caller to provide an implicit (at the call site).
When you write
def test[C[X] <: TraversableOnce[X]] =
implicitly[CanBuildFrom[C[Int], Int, C[Int]]]
You are asking the compiler with the call implicitly
to look-up an implicit already in scope. But you are not having any implicit of the given type in scope! So the two definitions of test
are doing something entirely different.
You use implicitly
normally to get hold of an implicit to which you don't have the name, because it was specified using the context-bounds or type-class notation, like def test[A: TypeClass]
. You cannot use that notation here because CanBuildFrom
has three type parameters and not one. So you cannot do much with implicitly
here.
You could use implicitly
with your first case:
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)
}
But then you already know that you have that implicit with name cbf
...
Note that you can get hold of an implicit CanBuildFrom
for a known collection type:
implicitly[CanBuildFrom[List[Int], Int, List[Int]]] // works!
But this doesn't work if your collection type (C[X]
) is abstract.
Upvotes: 2