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?
The first variant is straightforward:
println
on x
.println
(this prints the extra newline).With the second variant you effectively tell Scala to do this:
println()
.
Subsequently, do nothing with this newly created object. 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.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With