"For comprehension" is a another syntaxe to use map
, flatMap
and withFilter
(or filter) methods.
yield
keyword is used to aggregate values in the resulting structure.
This composition can be used on any type implementing this methods, like List
, Option
, Future
...
case class Book(author: String, title: String)
for {
book <- books
if book.author startsWith "bob"
} yield book.title
is the same as
books.withFilter(book => book.author startsWith "bob").map(book => book.title)
case class Book(authors: List[String], title: String)
for {
book <- books
author <- book.authors
if author startsWith "bob"
} yield book.title
is the same as
books.flatMap(book => book.authors.withFilter(author => author startsWith "bob").map(author => book.title))
val optA : Option[String] = Some("a value")
val optB : Option[String] = Some("b value")
I want an option tuple : Option[(String, String) composing this two options, i.e. Some(("a value","b value"))
:
for {
a <- optA
b <- optB
} yield (a,b)
is the same as
optA.flatMap(a => optB.map(b => (a, b)))
You can also filter options with if/withFilter :
for {
a <- optA
if(a startsWith "a")
b <- optB
} yield (a,b)
Or
optA.withFilter(a=> a startsWith "a").flatMap(a => optB.map(b => (a, b)))
If you change "a" to "c" in the condition, the resulting value will be None
instead of Some(("a value","b value"))
.
You can mix options and list types in map/flatMap (Option can be seen as a simple collection) :
val optNumbers = List(Some(1), Some(2), None, Some(3))
We want to remove empty values and increment other values : List(2, 3, 4)
:
for {
optNumber <- optNumbers
value <- optNumber
} yield value +1
is the same as
optNumbers.flatMap(optNumber => optNumber.map(value => value+1))
Beautiful Monadic pattern.