隐式调用时找不到 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]
) 是抽象的,这将不起作用。
考虑以下 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]
) 是抽象的,这将不起作用。