Developer Blog

The Many Ways to Use Option in Scala

There are many way to use Option in Scala. Per scala docs, the idiomatic way to use Option is to treat it as a collection that can have either a length of one of zero.

Let’s says we have a basic class, Author

Simple Option checking

1
2
case class Author(name:Option[String])
val author:Author = ...

The traditional way is to check if the name is defined. This way can be prone to error is you forget to add the “get” method

1
val authorName:String = if (author.name.isDefined) author.name.get else "name is empty"

A best way to check for a simple Option is GetOrElse

1
val authorName:String = author.name.getOrElse("name is empty")

In Scala 2.10, the fold method was introduced for Option. You could also use the following syntax:

1
val authorName:String = author.name.fold("name is empty")

It saves a little bit of typing, but not much difference, it might confuse those that are not familiar with it.

You could also use pattern matching on Option, which is generally discouraged, since it is rather verbose.

1
2
3
4
5
val authorName:String =
  author.name match {
    case Some(name) => name
    case _ => "name is empty"
  }

Nested Options

Let’s say our author variable is now an option:

1
val author:Option[Author] = ...

Using the isDefined method is getting rather verbose and error prone here.

1
val authorName:String = if (author.isDefined && author.get.name.isDefined) author.get.name.get else "empty"

You could use getOrElse here, but you would still need to check is the the author is defined. Still rather clunky.

1
val authorName:String = if (author.isDefined) author.get.name.getOrElse("empty")

The most idiomatic way to use nested options is map. The following will return the name if both author and author.name are present

1
val authorName:Option[String] = author.map(_.name)

Or:

1
val authorName:String = author.map(_.name).getOrElse("name is empty")

Multiple Nested Options

Flatmap

Let’s up the ante here.

1
2
3
case class Author(name:Option[Name])
case class Name(firstName:Option[String], lastName:Option[String])
val author:Option[Author] = ...

In this scenario, author, author.name, and author.firstName could be None. FlatMap to the rescue.

1
val authorName = author.flatMap(_.name).flatMap(_.firstName)

or

1
val authorName = author.flatMap(_.name).flatMap(_.firstName).getOrElse("first name is empty")

flatMap reduces the nested options, allowing us to retrieve what we need. No need to use any if statements, it is type safe.

For-Comprehension

Another way is to use a for-comprehension. It is a little more verbose, but allows us to make the code a little cleaner and easy to maintain. It is best used when dealing with multiple levels of Options.

1
2
3
4
5
val authorName:Option[String] = for {
  author <- author
  name <- author.name
  firstName <- name.firstName
} yield firstName

or

1
2
3
4
5
val authorName:String = (for {
  author <- author
  name <- author.name
  firstName <- name.firstName
} yield firstName).getOrElse("First name is empty")