The term comprehension comes from functional programming. It expresses the idea that we are traversing one or more collections of some kind, “comprehending” what we find, and computing something new from it, often another collection.
1. for
loops
Let’s start with a basic for
expression:
scala> val list: List[Int] = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> for (ele <- list) println(ele)
1
2
3
scala> for {ele <- list} println(ele) // Curly braces also is OK.
1
2
3
Because this form doesn’t return anything, it only performs side effects. These kinds of for comprehensions are sometimes called for loops, analogous to Java for loops.
The expression ele <- list
is called a generator expression, so named because it’s generating individual values from a collection. The left arrow operator (<-
) is used to iterate through a collection, such as a List
.
An informal convention is to use parentheses when the for comprehension has a single expression and curly braces when multiple expressions are used.
2. Guards: Filter Values
We can add if
expressions to filter for just elements we want to keep. These expressions are called guards.
One guards
scala> for (ele <- list if ele!= 2) println(ele)
1
3
More than one guards
scala> for (ele <- list if ele!= 2 && ele!=3) println(ele)
1
scala> for (ele <- list if ele!= 2 if ele!=3) println(ele)
1
3. Yielding
What if, rather than printing your filtered collection, you needed to hand it off to another part of your program? The yield
keyword is your ticket to generating new collections with for expressions.
scala> for (ele <- list if ele!=2) yield ele
res11: List[Int] = List(1, 3)
When a for comprehension doesn’t use yield
, but performs side effects like printing instead, the comprehension is called a for loop, because this behavior is more like the for loops you know from Java and other languages.
4. Expanded Scope and Skip None
(1) Expanded Scope
Another useful feature of Scala’s for comprehensions is the ability to define values inside the first part of your for expressions that can be used in the later expressions., as in this example:
for (ele <- list) println(ele)
Note that ele
is an immutable value, but the val
keyword isn’t required.
(2) Skip None
If you recall Option
, which we discussed as a better alternative to using null
, it’s useful to recognize that it is a special kind of collection, limited to zero or one elements. We can “comprehend” it too:
val dogBreeds = List(Some("Doberman"), None, Some("Yorkshire Terrier"),
Some("Dachshund"), None, Some("Scottish Terrier"),
None, Some("Great Dane"), Some("Portuguese Water Dog"))
println("first pass:")
for {
breedOption <- dogBreeds
breed <- breedOption // Get text from Option.
upcasedBreed = breed.toUpperCase()
} println(upcasedBreed)
println("second pass:")
for {
Some(breed) <- dogBreeds
upcasedBreed = breed.toUpperCase()
} println(upcasedBreed)
Imagine that we called some services to return various breed
names. The services returned Options
, because some of the services couldn’t return anything, so they returned None
.
In the first expression of the first for
comprehension, each element extracted is an Option
this time. The next line uses the arrow to extract the value in the option
.
The second for
comprehension makes this even cleaner, using pattern matching. The expression Some(breed) <- dogBreeds
only succeeds when the breedOption
is a Some
and it extracts the breed
, all in one step.
Note:
Doesn’t None
throw an exception if you try to extract an object from it? Yes, but the comprehension effectively checks for this case and skips the Nones
.
Q: When do you use the left arrow (<-) versus the equals sign (=)?
A: You use the arrow when you’re iterating through a collection or other “container,” like an Option
, and extracting values. You use the equals sign when you’re assigning a value that doesn’t involve iteration.
Note
1. A limitation is that the first expression in a for
comprehension has to be an extraction/iteration using the arrow.
2. Scala for
comprehensions do not offer a break
or continue
feature. Other features make them unnecessary.
Ref