I want to execute a function three times. That function happens to return a string.
def doThingReturnString(): String = {
println("Did a thing, returned a string.")
"abcdef"
}
(1 to 3).foreach { n =>
doThingReturnString()
}
(1 to 3).foreach {
doThingReturnString()
}
I expect both loops to print three lines. Instead, the first loop prints three lines and the second loop prints one.
Did a thing, returned a string.
Did a thing, returned a string.
Did a thing, returned a string.
Did a thing, returned a string.
Why does naming the parameter cause the loop to only execute once?
foreach
expects a function Int => U
(where U
can be "whatever"). Period. If you want to ignore the parameter, use an underscore.
(1 to 3).foreach { _ => doThingReturnString() }
When you write
(1 to 3).foreach { doThingReturnString() }
The braces act like parentheses
(1 to 3).foreach(doThingReturnString())
The argument for foreach
must be Int => U
, but here, it is a String
. A String
can be implicitly converted to an Int => U
, because a String
can implicitly convert to WrappedString
, which treats it as a collection type, specifically as a Seq[Char]
, which can be upcast to a PartialFunction[Int, Char]
from indices to elements, which can be upcast to Int => Char
. Thus, you've essentially written
val temp = doThingReturnString()
(1 to 3).foreach { i => temp.charAt(i) }
The reason for this behavior is that treating Seq[A]
s as PartialFunction[Int, A]
s is pretty sensible. Also sensible is being able to treat strings like the other collection types, so we have an implicit conversion to augment Java's String
with Scala's collection architecture. Putting them together, so that String
s turn into Int => Char
s, produces somewhat surprising behavior.
Let's change your expression to:
(1 to 3).foreach { "abc"}
Can you guess the result? It is
java.lang.StringIndexOutOfBoundsException: String index out of range: 3
If we change it to
(1 to 3).foreach { "abcd"}
the program executes without the exception. So, in case of your expression:
(1 to 3).foreach {
doThingReturnString()
}
you: firstly execute doThingReturnString()
, which returns a string "abcdef"
. Then, for each number i
in the range 1 to 3
, the compiler executes "abcdef"(i)
.
As to why (1 to 3).foreach { n => doThingReturnString() }
is seemingly treated differently from (1 to 3).foreach { doThingReturnString() }
, the best explanation I know comes from the book Scala Puzzlers (p. 20; no affiliation with the authors):
Since anonymous functions are often passed as arguments, it’s common to see them surrounded by { ... } in code. It’s easy to think that these curly braces represent an anonymous function, but instead they delimit a block expression: one or multiple statements, with the last determining the result of the block.
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