在Scala中,当使用部分应用函数与curried函数时,我们必须处理不同的处理类型推断的方法。让我用一个例子来展示它,使用一个基本的过滤功能(例子来自优秀的Functional Programming in Scala书):
1)部分应用功能
def dropWhile[A](l: List[A], f: A => Boolean): List[A] = l match {
case Nil => Nil
case x::xs if (f(x)) => dropWhile(xs, f)
case _ => l
}
2)咖喱部分应用功能
def dropWhileCurried[A](l: List[A])(f: A => Boolean): List[A] = l match {
case Nil => Nil
case x::xs if (f(x)) => dropWhileCurried(xs)(f)
case _ => l
}
现在,虽然两个版本的实现相同,但是当我们调用这些函数时会出现差异。虽然 curried 版本可以简单地称为:
dropWhileCurried(List(1,2,3,4,5))(x => x < 3)
同样的方法(省略 x 的类型)不能与非咖喱的一起使用:
dropWhile(List(1,2,3,4,5), x => x < 3)
<console>:9: error: missing parameter type
dropWhile(List(1,2,3,4,5), x => x < 3)
因此必须使用此表格:
dropWhile(List(1,2,3,4,5), (x: Int) => x < 3)
我理解就是这种情况,而且我知道关于这个事实还有其他问题,但我想要理解的是为什么就是这种情况。在类型推断方面,Scala编译器将这两种类型的部分应用函数区别对待的原因是什么?
答案 0 :(得分:1)
首先,您的两个示例都不是部分应用的功能。部分应用的函数(不要与部分函数混淆)是其中只应用了部分参数的函数,但是你已经准备好了所有的参数。
但是你可以很容易地将第二个例子变成部分应用的函数(和curried):val a = dropWhileCurried(List(new B, new B))_
。现在你有a
只应用了第一个参数,你需要应用第二个来执行它:println(a(x => true))
。您可以使用第一个示例执行相同的操作:val a = dropWhile(List(new B, new B), _: B => Boolean)
。
现在至于推论以及为什么它会这样:我只能假设,但这对我来说听起来很合理。您可以将函数中的每个参数视为相等,但是如果推理起作用并且您编写了dropWhile(List(new B, new B), _ => true)
,则假设_
的类型为B
,但是如果dropWhile(List(new B, new B), _: A => true
也可以B extends A
。在这种情况下,如果您更改参数的顺序,则推断会发生变化,或者它根本不会起作用:dropWhile(_ => true, List(new B, new B))
并且它会定义使推理非常复杂,因为编译器必须扫描定义几个次。
现在,如果您回到部分应用程序并考虑致电dropWhileCurried(xs)(f)
,请始终将xs
部分应用于dropWhileCurried
,然后将f
应用于结果以前的操作,听起来合情合理。编写器需要在编写dropWhileCurried(xs)
时推断类型,因为这是一个部分应用程序(尽管我最后仍然缺少_
)。所以现在,当推断出类型时,它可以继续并将(f)
应用于它。
这至少是我如何看待这个问题。可能有更多的理由,但如果你没有得到更好的答案,这应该有助于理解一些背景知识。