我试过了,但失败了,错误:缺少扩展函数的参数类型 ((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)
最佳答案
这是因为“必须完全知道匿名函数的参数类型”(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 中,匿名函数只是(像任何函数一样)一个对象。创建匿名函数意味着实例化一个通用类(扩展 Function*
),其中函数参数的类型被编码为 Function*
的类型参数。类(再读一遍,我保证这句话是有道理的)。如果不完全指定类型参数,就不可能实例化任何泛型类。函数也不异常(exception)。
正如 Impredicative 在评论中显示的那样,显式指定匿名函数参数的类型可以修复编译错误:
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
类型参数,并将这些其他方法迁移到那里。您不需要将所有的扩充内容放在同一个隐式类中。
关于scala - 为什么 Scala 不能从隐含证据中推断出类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15093071/