Why does foreach only execute once when param name isn't specified?

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 Strings turn into Int => Chars, 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.

Tags:

Loops

Scala