Summary: In this article I show two ways to extract information from nested optional fields in your Scala domain models. This example is a little contrived, but if you have a situation where one Option instance contains one or more other Options, this article may be helpful.
Optional fields in your domain model
There are times when you’re creating your domain model when it makes sense to use optional fields in your case classes. For instance, when you model an Address, the “second street address” isn’t needed for all people, so making it an optional field makes sense:
case class Address(
street1: String,
street2: Option[String],
city: String,
state: String,
zip: String
)
True confession: When I first started working with Scala I used to hate using
Optionfields incaseclasses. But once I (a) swore off usingnullvalues, and (b) learned how to properly work withOptions, I saw the beauty of the approach. For instance, one look at this class definition tells you that there may or may not be astreet2field; as theOptionwrapper type implies, its optional.
Taking this a step further, it’s also possible that when you create a new Person instance in an application, you might not know the person’s address, so that field may also be optional:
case class Person(
name : String,
address : Option[Address]
)
So now Address has the optional field street2, and Person has the optional field address.
When Options contain other Options (nested Options)
Now imagine a case where you have an Option[Person] in an application. For instance, maybe you’re getting a list of all of the salespeople for a region, and because some positions may not be filled, that query returns a list of Option[Person], i.e., a List[Option[Person]]. Next, further assume that you’re working on a piece of code that needs to get the street2 portion of each person’s address. How would you do this?
When you start writing this function, you know that you’ll be given an Option[Person] as input, so you can begin to sketch the function signature like this:
def getStreet2(maybePerson: Option[Person]) ...
Next, you know that street2 is optional — an Option[String] — so you can add the function’s return type:
def getStreet2(maybePerson: Option[Person]): Option[String] = ...
Now all you have to do is to fill out the body of the function to match that signature.
Using flatMap
One way to handle Options that contains other Options is to use flatMap. This solution shows how to flatMap each Option to get the result you want:
def getStreet2(maybePerson: Option[Person]): Option[String] = {
maybePerson flatMap { person =>
person.address flatMap { address =>
address.street2
}
}
}
While most people prefer for expressions — more on that shortly — this is a common use of flatMap. Depending on your needs, you can use a similar approach with any monad, i.e., Try, Either, List, Future, etc.
Here’s a quick explanation of the getStreet2 code:
maybePersonis anOption, so youflatMapit to get aPersoninstance- Because
addressinPersonis also anOption, youflatMapit to get anAddressinstance - Once you have the
address, you yieldaddress.street2
As I wrote in my book, Functional Programming, Simplified, when you write code like this, all you have to do is focus on the “happy path,” i.e., the success case. In this algorithm, the success case means that all of those Options will return Some instances, and you’ll get the street2 field in the end. As I also mention in the book, the “unhappy path” takes care of itself.
Note: There is a cleaner version of this approach in the Comments section below.
Some sample data
A couple of examples will show how getStreet2 works. First, in this example, I use None for the street2 field, so getStreet2 yields a None:
scala> val a1 = Address("123 Main Street", None, "Talkeetna", "Alaska", "99676")
a1: Address = Address(123 Main Street,None,Talkeetna,Alaska,99676)
scala> val p = Person("Al", Some(a1))
p: Person = Person(Al,Some(Address(123 Main Street,None,Talkeetna,Alaska,99676)))
scala> getStreet2(Some(p))
res0: Option[String] = None
Second, in this example I do have a street2 address (“Apt. 1”), so getStreet2 yields a Some:
scala> val a2 = Address("123 Main Street", Some("Apt. 1"), "Talkeetna", "Alaska", "99676")
a2: Address = Address(123 Main Street,Some(Apt. 1),Talkeetna,Alaska,99676)
scala> val p = Person("Al", Some(a2))
p: Person = Person(Al,Some(Address(123 Main Street,Some(Apt. 1),Talkeetna,Alaska,99676)))
scala> getStreet2(Some(p))
res0: Option[String] = Some(Apt. 1)
This shows that getStreet2 works as desired.
But if you don’t like flatMap ...
Most humans like for expressions
While my brain is at a point where the flatMap code now makes sense and is readable, most people prefer for expressions to flatMap, so I suspect that they’d write getStreet2 like this:
def getStreet2(maybePerson: Option[Person]): Option[String] = {
for {
person <- maybePerson
address <- person.address
street2 <- address.street2
} yield street2
}
If you take the time to use my sample data with that function in the REPL, you’ll see that it works just like the flatMap version of getStreet2.
Motivations
My main reason for writing this article was to demonstrate two ways to deal with Option instances that contain other Options. As shown, you can use (a) flatMap or (b) a for expression to traverse the Options to get the value you want.
A second reason for writing this article was to give you a little more exposure to flatMap. When I first learned Scala, I always thought of flatMap in terms of collections classes, but once you get into other monadic data types like Option, Try, Either, etc., you realize that there’s a very different way to think about flatMap, as shown in this example.
For those who have read Functional Programming, Simplified, you might know that I have a cheeky grin on my face when I use the term, “monadic,” so here’s a ;) for you.
See also
This article was inspired by an article titled, FP for the average Joe - II - ScalaZ Monad Transformers.

