For Comprehensions
<div class="callout callout-info"> In addition to the standard imports given at the start of the chapter, in this section we're assuming the following:
import doodle.random._
</div>
Scala provides some special syntax, called a for comprehension, that makes it simpler to write long sequences of flatMap
and map
.
For example, the code for randomConcentricCircles
has a call to flatMap
and map
.
def randomConcentricCircles(count: Int, size: Int): Random[Image] =
count match {
case 0 => Random.always(Image.empty)
case n =>
randomCircle(size, randomPastel).flatMap{ circle =>
randomConcentricCircles(n-1, size + 5).map{ circles =>
circle.on(circles)
}
}
}
This can be replaced with a for comprehension.
def randomConcentricCircles(count: Int, size: Int): Random[Image] =
count match {
case 0 => Random.always(Image.empty)
case n =>
for {
circle <- randomCircle(size, randomPastel)
circles <- randomConcentricCircles(n-1, size + 5)
} yield circle.on(circles)
}
The for comprehension is often easier to read than direct use of flatMap
and map
.
A general for comprehension
for {
x <- a
y <- b
z <- c
} yield e
translates to:
a.flatMap(x => b.flatMap(y => c.map(z => e)))
Which is to say that every <-
, except the last, turns into a flatMap
, and the last <-
becomes a map
.
For comprehensions are translated by the compiler into uses of flatMap
and map
.
There is no magic going on.
It is just a different way of writing code that would use flatMap
and map
that avoids excessive nesting.
Note that the for comprehension syntax is more flexible than what we have presented here.
For example, you can drop the yield
keyword from a for comprehension and the code will still compile.
It just won't return a result.
We're not going to use any of these extensions in Creative Scala, however.