Ome*_*ten 3 scala type-inference
我试过这个并且它失败并出现错误:扩展函数缺少参数类型((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)
}
Run Code Online (Sandbox Code Playgroud)
(使用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*(再次读取它,我保证这句话有意义).在没有完全指定类型参数的情况下,根本不可能实例化任何泛型类.功能也不例外.
正如Impredicative在注释中所示,显式指定匿名函数参数的类型可修复编译错误:
List(("a", 1), ("b", 2)).foo((a : Iterable[Int]) => a.sum)
Run Code Online (Sandbox Code Playgroud)
甚至:
List(("a", 1), ("b", 2)).foo((_.sum):Iterable[Int] => Int)
Run Code Online (Sandbox Code Playgroud)
但在您的情况下,修复问题似乎很简单,而无需显式指定匿名函数的类型:
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
}
Run Code Online (Sandbox Code Playgroud)
也许你为什么使用单一类型参数的原因T(参数代替A,并B有证据表明,在上面的示例所示)T <: (A, B)是在您的实际代码你在课堂上的其他方法Repro不需要T对是一对.在这种情况下,只需Repro2使用T类型参数创建另一个隐式类,然后在那里迁移其他方法.您不需要将所有的丰富内容放在同一个隐式类中.