3

我已经尝试过了,但失败并出现错误:缺少扩展函数的参数类型 ((x$29) => x$29.sum)

有人可以解释为什么会这样吗?这仅仅是Scala的类型推断不够强大吗?

object HelloStackOverflow {
    implicit class Repro[T](val iterable: Iterable[T]) extends AnyVal {
        def foo[A, B, Z](bar: Iterable[B] => Z)(implicit evidence: T <:< (A, B)) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum)
}

(使用 Scala 2.10)

4

1 回答 1

7

这是因为“必须完全知道匿名函数的参数类型”(Scala 语言规范 8.5)。

当一个方法接受一个匿名函数时,scala 使用它知道参数类型的事实来让调用者省略匿名函数参数的类型(让你写类似的东西x => x+1代替x: Int => x+1,或_.sum代替x: Iterable[Int] => x.sum。这是其中之一在 scala 中很好的推理应用。但显然,这首先需要知道匿名函数的确切预期类型,这不是这里的情况:匿名函数的参数bar 是类型Iterable[B].B是一个自由类型变量,不能以任何方式从早期的参数列表中推断出来(方法中没有先前的参数列表foo)。所以根本不可能知道B 匿名函数中的类型(_.sum)可以推断,这会触发错误,因为知道规范要求的确切类型。

这是很合乎逻辑的。在 Scala 中,匿名函数只是(像任何函数一样)一个对象。创建一个匿名函数意味着实例化一个泛型类(即 extends Function*),其中函数参数的类型被编码为类的类型参数Function*(再读一遍,我保证这句话是有道理的)。如果不完全指定类型参数,就不可能实例化任何泛型类。函数也不例外。

正如在评论中显示的那样,显式指定匿名函数参数的类型修复了编译错误:

List(("a", 1), ("b", 2)).foo((a : Iterable[Int]) => a.sum)

甚至:

List(("a", 1), ("b", 2)).foo((_.sum):Iterable[Int] => Int)

但是在您的情况下,无需显式指定匿名函数的类型即可解决问题似乎很简单:

object HelloStackOverflow {
    implicit class Repro[A,B](val iterable: Iterable[(A,B)]) extends AnyVal {
        def foo[Z](bar: Iterable[B] => Z) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum) // works like a charm
}

也许您使用单个类型参数T(而不是参数AB如上面的示例)的原因T <: (A, B)是,在您的真实代码中,您在类Repro中有其他不需要T成对的方法。Repro2在这种情况下,只需创建另一个带有类型参数的隐式类T,然后将这些其他方法迁移到那里。您不需要将所有丰富内容放在同一个隐式类中。

于 2013-02-26T16:53:15.827 回答