Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructing a lambda expression using an underscore

This code

(1 to 30).foreach { x =>
  println(x)
  println
}

does what I'd expect: it prints each of 1 to 30, interspersed with blanks. I'm pretty clear on what's going on here, I think: I'm passing an anonymous function that first prints its argument, and then prints a blank line.

What I don't understand is why this doesn't do the same:

(1 to 30).foreach {
  println _
  println
}

It looks equivalent to me. The underscore should represent the first and only argument to the function; and the function prints its argument, and then prints a blank line. But when I run this second version, I don't get the blank lines.

What causes this difference?

like image 804
chiastic-security Avatar asked Dec 10 '14 12:12

chiastic-security


3 Answers

The first variant is straightforward:

  1. In the first line, apply println on x.
  2. In the second line, apply the no-argument println (this prints the extra newline).

With the second variant you effectively tell Scala to do this:

  1. In the first line, define a function object from println(). Subsequently, do nothing with this newly created object.
  2. In the second line, apply println to the argument (the element of the sequence).

The confusion stems from the assumption that println(x) and println _ are equivalent. They are different. The funcId _ syntax defines a new function based on funcId, it is not the same as using the "underscore argument" notation when calling a function.

like image 194
mikołak Avatar answered Nov 11 '22 18:11

mikołak


There is a number of things going on here.

First, of all the parameter placeholder syntax can only be used within outer parentheses of the lambda definition. It cannot be used within parentheses of the method calls that you perform within the lambda definition.

Here is an example to demonstrate this point.

val a = (1 to 10).map(_ + 1)

This will work.

val b = (1 to 10).map(math.sin(_ + 1))

This will not work.

Therefore your code does not use parameter placeholder syntax at all. It instead uses partially applied functions.

For example

(1 to 10).foreach(println _)

is functionally equal to

val a = println (_ : Int)
(1 to 10).foreach(a)

Also when a method name is used within lambda expression the underscore can be omitted. Scala will still generate the partially applied method.

Therefore

(1 to 10).foreach(println)

is equal to

(1 to 10).foreach(println _)

And therefore your code is equal to

val a = println (_ : Int)
  (1 to 10).foreach{
    a
    a
  }

And because {a a} returns a, it is equal to

val a = println (_ : Int)
(1 to 10).foreach(a)
like image 36
Dennis Avatar answered Nov 11 '22 18:11

Dennis


To add to other answers, there actually exists a way to use println(_) and not to declare x parameter:

(1 to 30).foreach {
  println(_: Int)
  println
}

Here foreach parameter is function, which firstly invokes println(_) for range element, and then passes println(Int) result (which is (): Unit) to another function, _ => println, which ignores it's argument and prints new line.

like image 40
Evgeny Veretennikov Avatar answered Nov 11 '22 19:11

Evgeny Veretennikov