For Comprehensions in Scala
What is a
Just like imperative programming languages, Scala provides a for-loop,
but the similarities end there. Primarily it is used for iterating
collections, and to extract items out of them. The benefit of
that its clear and concise.
Taking the example of creating a list of all the indices of a matrix, we will do something like this in Scala:
for comprehensions, we can do something like:
This is both clearer and abstracts away the details of
map=s, which at this point seems at a lower level than =for.
What we saw above is essentially what happens when
for is used, but in
the opposite direction, i.e. the
for is changed to the
map=s at compile time. Therefore, =for is not a new construct in the language,
but just a transformation to
map and other functions.
The conversion is done recursively, so that each
for is converted to a
simpler expression (which can be yet another
for) until no more
Syntactically, the for comprehensions are of 3 types (although both 1 and 2 are weak cases of 3):
These are converted to a combinations of
withFilter during compile time. Let’s take each type and see how it is
Type 1: A single collection:
Looking hard enough, we see that iterating over a collection is just another way of =map=ping over a collection. And the simplest conversion is essentially that:
Type 2: Filtering over a collection:
for comprehension contains an
if guard, then that is converted
to a simpler expression using a
The benefit of using a
withFilter is that it’s:
- lazy and
- only creates the intermediate collection once.
will create a copy of the collection for their calls. But
collection.withFilter(pred).map(transform) will just work without
creating a new collection for each call, and will be evaluated only when
Type 3: More than one collection:
If there is more than one collection, then the outer collection is
converted to a
flatMap, and its elements are passed over to inner
collection which yield the values from both the collections.
flatMap because we do not want an unnecessary nesting of the
collection. In the following code, if we use
map, then we will get a
nested collection instead of a linear/1-dimensional collection.
So if you noted above, each type gets converted to a simpler type.
- Type1 removes the
- Type2 removes the
- Type3 removes 1 out of 2 collections.
Let’s take an example to convert a
for into the more basic types:
Since this holds 2 collection, the first step is to convert the
0 until row range into a flatMap by Type3 expansion:
The second step is to convert the
if guard into a
expression using Type2 expansion. Notice that
withFilter is applied on
the collection which had the guard, and not on any other collection:
Now we have only one
for comprehension remaining, which is of the form
Type1 – just a single collection, so we will change that to a
So it is this way that Scala allows
for comprehension using just the
One more thing which is clear from here is that
for comprehension by
itself is not lazy, because
flatMap, are not lazy as well. But
if the underlying collection is lazy, then
for becomes lazy as well.
In another post, I’ll discuss how any class which defines these 3
methods can provide iteration based on the
Author Tushar Tyagi
LastMod Sep 2, 2016