【问题标题】:CanBuildFrom not found when invoked implicitly隐式调用时找不到 CanBuildFrom
【发布时间】:2017-05-16 21:55:54
【问题描述】:

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

【问题讨论】:

    标签: scala scala-collections


    【解决方案1】:

    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 来获取您没有名称的隐式,因为它是使用上下文边界或类型类表示法指定的,例如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 中执行此操作的,因此在第一种情况下,当我调用该函数时,会找到一个通用的 CanBuildFrom(我想是在 Predef 中)。这意味着,在作用域中有一个合适的 CanBuildFrom 隐式实例。为什么在 REPL 中定义独立函数时不能使用同一个?
    • 在一种情况下,您有一个具体的类型,例如List,在另一个你有一个抽象类型参数C[_]。除其他外,隐式解析将查看类型类 (CanBuildFrom) 及其类型参数的伴随对象(例如这里 List - 它找到类型类实例的地方!),但显然无法解析抽象类型.
    • 对,现在说得通了。
    • 另见此处:docs.scala-lang.org/tutorials/FAQ/finding-implicits.html(“类型参数的隐式范围”)
    猜你喜欢
    • 1970-01-01
    • 2015-06-16
    • 2012-04-15
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多