在 GitHub 上编辑此页面

自动 Eta 扩展 - 更多细节

动机

Scala 在方法函数之间保持着方便的区分。方法是类定义的一部分,可以在对象中调用,而函数本身是完整的对象,使其成为一等实体。例如,它们可以被分配给变量。这两种机制在 Scala 中通过一种称为eta 扩展(也称为 eta 抽象)的机制连接起来,它将对方法的引用转换为函数。直观地说,方法m可以通过将其转换为对象来传递:函数x => m(x)

在这个将方法分配给val的代码片段中,编译器将执行自动 eta 扩展,如注释所示

def m(x: Int, y: String) = ???
val f = m // becomes: val f = (x: Int, y: String) => m(x, y)

在 Scala 2 中,方法引用m仅在预期类型为函数类型时才会转换为函数值,这意味着上面的示例中的转换不会被触发,因为val f没有类型声明。为了仍然获得 eta 扩展,快捷方式m _将强制转换。

对于像上面示例中那样具有一个或多个参数的方法,现在已经取消了此限制。m _语法不再需要,并且将在将来被弃用。

自动 eta 扩展和部分应用

在以下示例中,m可以被部分应用于前两个参数。将m分配给f1将自动执行 eta 扩展。

def m(x: Boolean, y: String)(z: Int): List[Int]
val f1 = m
val f2 = m(true, "abc")

这将创建两个函数值

f1: (Boolean, String) => Int => List[Int]
f2: Int => List[Int]

自动 eta 扩展和隐式参数列表

具有隐式参数列表的方法将始终应用于隐式参数。

def foo(x: Int)(implicit p: Double): Float = ???
implicit val bla: Double = 1.0

val bar = foo // val bar: Int => Float = ...

自动 eta 扩展和上下文类型

具有上下文参数的方法可以通过显式编写预期上下文类型来扩展为上下文类型的值。

def foo(x: Int)(using p: Double): Float = ???
val bar: Double ?=> Float = foo(3)

规则

  • 如果m的 argument list 具有一个或多个参数,我们始终执行 eta 扩展
  • 如果m具有空的 argument list(即类型为()R
    1. 如果预期类型为() => T形式,我们执行 eta 扩展。
    2. 如果 m 由 Java 定义,或覆盖 Java 定义的方法,我们插入()
    3. 否则,我们将发出以下形式的错误:method must be called with () argument

因此,具有空 argument list 的未应用方法仅在预期函数类型时才会转换为函数。建议做法是显式地将方法应用于(),或使用() => m()将其转换为函数。

方法值语法m _已弃用。

参考

有关更多信息,请参阅PR #2701