隐式调用时找不到CanBuildFrom

时间:2017-01-02 20:18:07

标签: scala scala-collections

考虑以下REPL会话:

@ 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 个答案:

答案 0 :(得分:1)

implicitlyPredef)的定义是:

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来获取您没有名称的隐式,因为它是使用context-bounds或type-class表示法指定的,例如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])是抽象的,那么这不起作用。