处理部分应用函数时Scala编译器中类型推断限制的原因

时间:2015-11-17 12:32:01

标签: scala

在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编译器将这两种类型的部分应用函数区别对待的原因是什么?

1 个答案:

答案 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)应用于它。

这至少是我如何看待这个问题。可能有更多的理由,但如果你没有得到更好的答案,这应该有助于理解一些背景知识。