隐式调用时找不到 CanBuildFrom

CanBuildFrom not found when invoked implicitly

考虑以下 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 函数调用的,从理论上讲,它在隐式搜索范围方面的行为应该相同。是什么导致了这种行为?

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 来获取您没有名称的隐式,因为它是使用上下文边界或类型-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]) 是抽象的,这将不起作用。