这是因为“必须完全知道匿名函数的参数类型”(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
(而不是参数A
,B
如上面的示例)的原因T <: (A, B)
是,在您的真实代码中,您在类Repro
中有其他不需要T
成对的方法。Repro2
在这种情况下,只需创建另一个带有类型参数的隐式类T
,然后将这些其他方法迁移到那里。您不需要将所有丰富内容放在同一个隐式类中。