I want to iterate over a list of values using a beautiful one-liner in Scala.
For example, this one works well:
scala> val x = List(1,2,3,4)
x: List[Int] = List(1, 2, 3, 4)
scala> x foreach println
1
2
3
4
But if I use the placeholder _
, it gives me an error:
scala> x foreach println(_ + 1)
<console>:6: error: missing parameter type for expanded function ((x$1) =>x$1.$plus(1))
x foreach println(_ + 1)
^
Why is that? Can't compiler infer type here?
The underscore is a bit tricky. According to the spec, the phrase:
is equivalent to
Trying
yields:
If you add some types in:
The problem is that you are passing an anonymous function to
println
and it's not able to deal with it. What you really want to do (if you are trying to print the successor to each item in the list) is:This:
is equivalent to this:
There's no indication as to what might be the type of
x$1
, and, to be honest, it doesn't make any sense to print a function.You obviously (to me) meant to print
x$0 + 1
, wherex$0
would the the parameter passed byforeach
, instead. But, let's consider this...foreach
takes, as a parameter, aFunction1[T, Unit]
, whereT
is the type parameter of the list. What you are passing toforeach
instead isprintln(_ + 1)
, which is an expression that returnsUnit
.If you wrote, instead
x foreach println
, you'd be passing a completely different thing. You'd be passing the function(*)println
, which takesAny
and returnsUnit
, fitting, therefore, the requirements offoreach
.This gets slightly confused because of the rules of expansion of
_
. It expands to the innermost expression delimiter (parenthesis or curly braces), except if they are in place of a parameter, in which case it means a different thing: partial function application.To explain this better, look at these examples:
Here, we applies the second and third arguments to
f
, and returned a function requiring just the remaining argument. Note that it only worked as is because I indicated the type ofg
, otherwise I'd have to indicate the type of the argument I was not applying. Let's continue:Let discuss
k
in more detail, because this is a very important point. Recall thatg
is a functionInt => Int
, right? So, if I were to type1 + g
, would that make any sense? That's what was done ink
.What confuses people is that what they really wanted was:
In other words, they want the
x$1
replacing_
to jump to outside the parenthesis, and to the proper place. The problem here is that, while it may seem obvious to them what the proper place is, it is not obvious to the compiler. Consider this example, for instance:Now, if we were to expand this to outside the parenthesis, we would get this:
Which is definitely not what we want. In fact, if the
_
did not get bounded by the innermost expression delimiter, one could never use_
with nestedmap
,flatMap
,filter
andforeach
.Now, back to the confusion between anonymous function and partial application, look here:
The first line doesn't work because of how operation notation works. Scala just sees that
println
returnsUnit
, which is not whatforeach
expects.The second line works because the parenthesis let Scala evaluate
println(_)
as a whole. It is a partial function application, so it returnsAny => Unit
, which is acceptable.The third line doesn't work because
_ + 1
is anonymous function, which you are passing as a parameter toprintln
. You are not makingprintln
part of an anonymous function, which is what you wanted.Finally, what few people expect:
This works. Why it does is left as an exercise to the reader. :-)
(*) Actually,
println
is a method. When you writex foreach println
, you are not passing a method, because methods can't be passed. Instead, Scala creates a closure and passes it. It expands like this:There is a strange limitation in Scala for the nesting depth of expressions with underscore. It's well seen on the following example:
Looks like a bug for me.