som-snytt
som-snytt

Reputation: 39577

Placeholder syntax doesn't hold my place

I have already read some other questions like What are all the uses of an underscore in Scala? and while I'm sure this question has already been asked, I can't go through all of the other 17000 Scala questions.

There is Foreach have strange behaviour and Placeholder not useful but it still seems to be a hidden feature:

scala> val is = (1 to 5) toList
is: List[Int] = List(1, 2, 3, 4, 5)

scala> is foreach { i => println("Hi.") ; Console println 2 * i }
Hi.
2
Hi.
4
Hi.
6
Hi.
8
Hi.
10

scala> is foreach { println("Hi.") ; Console println 2 * _ }
Hi.
2
4
6
8
10

Can someone explain me please the difference?

If you feel a burst of enthusiasm and try:

scala> is foreach { i => println("Hi!") ; Console println 2 * i }
java.lang.IllegalArgumentException: !") ; Console println 2 * i }: event not found

then see this answer. And yes that really happened.

Upvotes: 2

Views: 204

Answers (2)

Rex Kerr
Rex Kerr

Reputation: 167891

Landei has the right answer, I think, but it's not unpacked very thoroughly. Let's start at the end:

scala> Console println 2 * _
<console>:8: error: missing parameter type for expanded function
((x$1) => Console.println(2.$times(x$1)))
              Console println 2 * _
                                  ^

Okay, so we see that on its own, Console println 2 * _ is trying to create a function via explicit eta expansion, except it doesn't know the return parameter type so it can't.

Now let's try a code block that returns a function.

scala> { println("Hi"); (i: Int) => i*5 }
Hi
res1: Int => Int = <function1>

So, as with everything, you execute the entire block (including side-effecting statements like println), and return the return value which is your function.

Now, as Landei said, placeholder syntax only works for one argument in one (simple) expression, and in the second case we don't have a simple expression but a block expression (consisting of two simple expressions). So we're not using placeholder syntax, we're creating a function. And we do it in a code block:

is foreach { println("Hi.") ; Console println 2 * _ }

which, since we don't start with function arguments, is interpreted as a plain parameter, except we can in almost any context--argument lists included--replace a simple expression (x) with a block expression { stuff; x }. So we can think of it as

is foreach ({ println("Hi.") ; Console.println 2 * _ })

Now the type inferencer knows what the return type is supposed to be, so it runs that code block which prints out "Hi" and creates a function, then passes that function (just once, at the beginning!) to foreach. If the type inferencer could look across lines to figure out types, it would be equivalent to this:

val temp = { println("Hi."); Console.println 2 * _ }
is foreach (temp)

Upvotes: 3

Landei
Landei

Reputation: 54574

Placeholder syntax works on one expression, not for the whole block, so your example is interpreted as { println("Hi."); i => Console println 2 * i }

Upvotes: 7

Related Questions