Scala: "map" vs "foreach" - is there any reason to use "foreach" in practice?

GreyCat picture GreyCat · Sep 10, 2014 · Viewed 10.2k times · Source

In Scala collections, if one wants to iterate over a collection (without returning results, i.e. doing a side effect on every element of collection), it can be done either with

final def foreach(f: (A) ⇒ Unit): Unit

or

final def map[B](f: (A) ⇒ B): SomeCollectionClass[B]

With the exception of possible lazy mapping(*), from an end-user perspective, I see zero differences in these invocations:

myCollection.foreach { element =>
  doStuffWithElement(element);
}

myCollection.map { element =>
  doStuffWithElement(element);
}

given that I can just ignore what map outputs. I can't think of any specific reason why two different methods should exist & be used, when map seems to include all the functionality of foreach, and, in fact, I would be pretty much impressed if an intelligent compiler & VM won't optimize out that collection object creation given that it's not assigned to anything, or read, or used anywhere.

So, the question is - am I right - and there are no reasons to call foreach anywhere in one's code?

Notes:

(*) The lazy mapping concept, as throughly illustrated in this question, might change things a bit and justify usage of foreach, but as far as I can see, one specifically needs to stumble upon a LazyMap, normal

(**) If one's not using a collection, but writing one, then one would quickly stumble upon the fact that for comprehension syntax syntax is in fact a syntax sugar that generates "foreach" call, i.e. these two lines generate fully equivalent code:

for (element <- myCollection) { doStuffWithElement(element); }
myCollection.foreach { element => doStuffWithElement(element); }

So if one cares about other people using that collection class with for syntax, one might still want to implement foreach method.

Answer

samthebest picture samthebest · Sep 10, 2014

I can think of a couple motivations:

  1. When the foreach is the last line of a method that is of type Unit your compiler will not give an warning but will with map (and you need -Ywarn-value-discard on). Sometimes you get warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses using map but wouldn't with foreach.
  2. General readability - a reader can know that your mutating some state without returning something at a glance, but greater cognitive resources would be required to understand the same operation if map was used
  3. Further to 1. you also can have type checking when passing named functions around, then into map and foreach
  4. Using foreach won't build a new list, so will be more efficient (thanks @Vishnu)