Haspemulator
Haspemulator

Reputation: 11318

CanBuildFrom not found when invoked implicitly

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

Answers (1)

0__
0__

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

Related Questions