为什么Scala不能从隐式证据中推断出类型

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)

Rég*_*les 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中,匿名函数就像一个对象(就像任何函数一样).创建一个匿名函数意味着实例化一个泛型类(扩展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类型参数创建另一个隐式类,然后在那里迁移其他方法.您不需要将所有的丰富内容放在同一个隐式类中.

  • 类型检查器是专门的定理证明器.定理证明通常具有严重的易处理性问题.这可能被视为一种权宜之计,但它也可能使得更多的程序易于类型检查,而不是"全局"(在整个方法的参数中)进行推理.可能有更好的理由,但我对基础理论的熟悉程度还远远不够. (3认同)