Scala:函数值的eta扩展(不是方法)

时间:2018-09-26 20:21:33

标签: scala function methods

在尝试使用scala的eta扩展后,我遇到了一个奇怪的功能。 让我们定义一个方法:

scala> def sum(a: Int, b: Int): Int = a + b
sum: (a: Int, b: Int)Int

好的,到目前为止,一切都很好。现在,使用eta扩展将其分配给val:

scala> val f = sum _
f: (Int, Int) => Int = $$Lambda$1051/694580932@55638165

现在,奇怪的事情来了。我可以再次将eta扩展应用于f,并且它正在工作(但是这会给我的方法增加麻烦):

scala> val g = f _
g: () => (Int, Int) => Int = $$Lambda$1055/1351568309@5602e540

这为什么起作用?我认为eta扩展仅对方法有效。 而且,我注意到这是不可能的:

scala> ((a: Int, b: Int) => a + b: Int) _
<console>:12: error: _ must follow method; cannot follow (Int, Int) => Int
       ((a: Int, b: Int) => a + b: Int) _
                         ^

但是与将eta扩展应用于f并不相同? 我有些困惑,这些eta扩展仍然对我隐藏了一些魔力。 非常感谢!

1 个答案:

答案 0 :(得分:5)

当您在REPL或对象/类的顶层编写val f = sum _时,Scala会定义一个访问器方法,以便您可以访问它。这是Scala如何消除这种情况的(通过scalac -Xprint:typer上的val f: (Int, Int) => Int = _ + _):

private[this] val f: (Int, Int) => Int = ((x$1: Int, x$2: Int) => x$1.+(x$2));
<stable> <accessor> def f: (Int, Int) => Int = Foo.this.f;

因此,当您随后编写val g = f _时,它正在对零参数访问器方法进行eta扩展,这将导致您看到的行为。要对此进行更多验证,请注意,如果将定义放入方法中,则会出现错误:

def foo = {
  val f: (Int, Int) => Int = _ + _
  val g = f _ // error: _ must follow method; cannot follow (Int, Int) => Int
}

这是因为仅为字段(和顶级REPL定义,将其视为字段)生成访问器。