Option
s have a flatMap
method. This means they can be used in a for comprehension. In this way we can lift regular functions to work on Option
s without having to redefine them.
val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = Option(2)
val myResult = for {
firstValue <- firstOption
secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = Some(3)
When one of the values is a None
the ending result of the calculation will be None
as well.
val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = None
val myResult = for {
firstValue <- firstOption
secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = None
Note: this pattern extends more generally for concepts called Monad
s. (More information should be available on pages relating to for comprehensions and Monad
s)
In general it is not possible to mix different monads in a for comprehension. But since Option
can be easily converted to an Iterable
, we can easily mix Option
s and Iterable
s by calling the .toIterable
method.
val option: Option[Int] = Option(1)
val iterable: Iterable[Int] = Iterable(2, 3, 4, 5)
// does NOT compile since we cannot mix Monads in a for comprehension
// val myResult = for {
// optionValue <- option
// iterableValue <- iterable
//} yield optionValue + iterableValue
// It does compile when adding a .toIterable on the option
val myResult = for {
optionValue <- option.toIterable
iterableValue <- iterable
} yield optionValue + iterableValue
// myResult: Iterable[Int] = List(2, 3, 4, 5)
A small note: if we had defined our for comprehension the other way around the for comprehension would compile since our option would be converted implicitly. For that reason it is useful to always add this .toIterable
(or corresponding function depending on which collection you are using) for consistency.