Scala : eta expansion of function values (not meth

2020-06-19 07:00发布

问题:

After experimenting with scala's eta expansion, I came across a weird feature. Let's define a method:

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

Ok, up until now, everything is fine. Now let's assign it to a val using eta expansion:

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

Now, the strange thing is coming. I can apply eta expansion again to f, and it is working (however it adds currying to my method) :

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

Why is this working ? I thought that eta expansion was only valid for methods. Moreover, I noticed that this is not possible:

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) _
                         ^

But is it not the same as applying eta expansion to f ? I am a bit confused and these eta expansions still hide some magic for me. Thanks a lot !

回答1:

When you write val f = sum _ at the top level of the REPL or an object/class, Scala defines an accessor method so that you can access it. Here is how Scala desugars this (via scalac -Xprint:typer on 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;

So, when you subsequently write val g = f _, it's doing eta-expansion on the zero-argument accessor method, which results in the behavior you see. For more verification of this, notice that, if you put the definitions in a method, you get an error:

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

This is because accessors are only generated for fields (and top-level REPL definitions, which are treated like fields).