6

Given

scala> def method(x: Int) = x
method: (x: Int)Int

scala> val func = (x: Int) => x
func: Int => Int = <function1>

Consider the following code:

scala> method _
res0: Int => Int = <function1>

scala> func(_)
res1: Int => Int = <function1>

scala> func _
res2: () => Int => Int = <function0>

I can understand that res0 is eta expansion and res1 is equivalent to lambda function (x) => func(x). But I cannot figure out the output of res2. Could anyone please explain that for me?

Lifu Huang
  • 11,930
  • 14
  • 55
  • 77

4 Answers4

8

This is actually a bit tricky. First, let's see what happens outside REPL:

It doesn't work when func is a local variable:

object Main extends App {
  def foo() = {
    val f = (_: Int) + 1
    f _
  }

  println(foo())
}

[error] /tmp/rendereraEZGWf9f1Q/src/main/scala/test.scala:8: _ must follow method; cannot follow Int => Int
[error]     f _
[error]     ^

But if you put it outside def foo, it compiles:

object Main extends App {
  val f = (_: Int) + 1
  val f1 = f _

  println(f1)
}

because f is both a field of Main and a method without arguments which returns the value of this field.

The final piece is that REPL wraps each line into an object (because Scala doesn't allow code to appear outside a trait/class/object), so

scala> val func = (x: Int) => x
func: Int => Int = <function1>    

is really something like

object Line1 {
  val func = (x: Int) => x
}
import Line1._
// print result

So func on the next line refers to Line1.func which is a method and so can be eta-expanded.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • So it turns out that it's still eta expansion working. Really great explanation, thank you! – Lifu Huang Sep 19 '16 at 15:07
  • Excellent analysis. Thanks. – Samar Sep 19 '16 at 15:35
  • Just curious, in regarding explanation "because f is both a field of Main and a method without arguments which returns the value of this field."..... .. question is, do you call f as 'method' in the same way as discussed here https://stackoverflow.com/a/2530007/4582240 ? to me it is not a method, instead is Value Declarations and Definitions (Function Type and Anonymous Functions). – soMuchToLearnAndShare Jul 23 '19 at 14:16
  • @MinnieShi Maybe I could have said "can be considered a method". See https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#method-values: "The expression `e _` is well-formed if `e` is of method type or if `e` is a call-by-name parameter." And `f` is certainly not a by-name parameter. – Alexey Romanov Jul 23 '19 at 16:24
  • 1
    @MinnieShi Also, yes it is a value declaration of function type; but that's also true in the local variable case, and the major point of my answer was to explain the difference between these cases. – Alexey Romanov Jul 23 '19 at 16:30
4

To answer your question about res2 - the appended underscore _ syntax is used to partially apply a function.

So.

scala> func _

Means you have partially applied your <function1>. This results in a new curried form, the first function of which takes zero arguments (hence <function0>)

() =>

Which returns your original <function1> which takes 1 argument.

Int => Int = <function1>

The complete result being the chain of functions.

res2: () => Int => Int = <function0>

The rule that might be useful for you to remember is that functions associate to the right so the following are equivalent.

() => Int => Int    
() => (Int => Int)

This other post might be useful reading for you.

Community
  • 1
  • 1
jacks
  • 4,614
  • 24
  • 34
3

You've used the eta Expansion to turn res2 into a function that takes 0 parameters and returns a function that takes a single parameter.

res2: () => Int => Int = <function0>

So you can now do this:

val zero = func _
val f: Int => Int = zero()
Luka Jacobowitz
  • 22,795
  • 5
  • 39
  • 57
  • So you mean it's eta expansion again? All I know about eta expansion is that it extracts the method value(i.e. a function) from a method. But I cannot see in my example why this applies. – Lifu Huang Sep 19 '16 at 13:22
  • You can add the underscore to any function. If you define a function `val x = (a: Int) => a` this: `val y = x _` works without a hitch and returns a `Function0`. – Luka Jacobowitz Sep 19 '16 at 14:07
  • Yep, I knew it.What I am asking is why this happens? – Lifu Huang Sep 19 '16 at 14:16
  • 1
    Hi @Luka, I did not understand what you meant at the beginning. After reading Alexey Romanov's answer, I see what's happening. You are right that it's just eta expansion. But I did not realize that each line in REPL will be wrapped into a object(I am new to Scala :P). Thank you for your answer. – Lifu Huang Sep 19 '16 at 15:16
2
val func1 = func _

This returns a function0 which takes no arguments and returns the func function.

You can use this like:

func1()(2) // outputs 2

You can continue doing this kind of expansion ad infinitum:

val func2 = func1 _

func2()()(2) // outputs 2
Samar
  • 2,091
  • 1
  • 15
  • 18
  • I saw what happens, but I cannot explain why. Is it a special syntax? or just a less obvious use of some known syntax? – Lifu Huang Sep 19 '16 at 13:24
  • I see what you are trying to understand. Seems like eta expansion is happening on functions too. But language specification says its only applicable on methods or call by name parameters of type: => T – Samar Sep 19 '16 at 13:35
  • Yep, so I am kind of curious about what is happening ; ) – Lifu Huang Sep 19 '16 at 13:52
  • Alexey Romanov gives a great explanation. It turns out that eta expansion is what's happening. Hope it helps! – Lifu Huang Sep 19 '16 at 15:12