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?
This:
x foreach println(_ + 1)
is equivalent to this:
x.foreach(println(x$1 => x$1 + 1))
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
, where x$0
would the the parameter passed by foreach
, instead. But, let's consider this... foreach
takes, as a parameter, a Function1[T, Unit]
, where T
is the type parameter of the list. What you are passing to foreach
instead is println(_ + 1)
, which is an expression that returns Unit
.
If you wrote, instead x foreach println
, you'd be passing a completely different thing. You'd be passing the function(*) println
, which takes Any
and returns Unit
, fitting, therefore, the requirements of foreach
.
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:
def f(a: Int, b: Int, c: Int) = a + b + c val g: Int => Int = f(_, 2, 3) // Partial function application g(1)
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 of g
, otherwise I'd have to indicate the type of the argument I was not applying. Let's continue:
val h: Int => Int = _ + 1 // Anonymous function, expands to (x$1: Int => x$1 + 1) val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped here val j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x$1 => x$1 + 1), so it misses the type of `x$1` val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x$1: Int => x$1 + 1), so you are adding a function to an `Int`, but this operation doesn't exist
Let discuss k
in more detail, because this is a very important point. Recall that g
is a function Int => Int
, right? So, if I were to type 1 + g
, would that make any sense? That's what was done in k
.
What confuses people is that what they really wanted was:
val j: Int => Int = x$1 => 1 + (x$1 + 1)
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:
def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))
Now, if we were to expand this to outside the parenthesis, we would get this:
def findKeywords(keywords: List[String], sentence: List[String]) = (x$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))
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 nested map
, flatMap
, filter
and foreach
.
Now, back to the confusion between anonymous function and partial application, look here:
List(1,2,3,4) foreach println(_) // doesn't work List(1,2,3,4) foreach (println(_)) // works List(1,2,3,4) foreach (println(_ + 1)) // doesn't work
The first line doesn't work because of how operation notation works. Scala just sees that println
returns Unit
, which is not what foreach
expects.
The second line works because the parenthesis let Scala evaluate println(_)
as a whole. It is a partial function application, so it returns Any => Unit
, which is acceptable.
The third line doesn't work because _ + 1
is anonymous function, which you are passing as a parameter to println
. You are not making println
part of an anonymous function, which is what you wanted.
Finally, what few people expect:
List(1,2,3,4) foreach (Console println _ + 1)
This works. Why it does is left as an exercise to the reader. :-)
(*) Actually, println
is a method. When you write x 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:
x.foreach(new Function1[Any,Unit] { def apply(x$1: Any): Unit = Console.println(x$1) })
The underscore is a bit tricky. According to the spec, the phrase:
_ + 1
is equivalent to
x => x + 1
Trying
x foreach println (y => y + 1)
yields:
<console>:6: error: missing parameter type x foreach println (y => y + 1)
If you add some types in:
x foreach( println((y:Int) => y + 1)) <console>:6: error: type mismatch; found : Unit required: (Int) => Unit x foreach( println((y:Int) => y + 1))
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:
x map (_+1) foreach println
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