Idiomatic Scala: Your Options Do Not Match

By marconilanna

Idiomatic Scala: Your Options Do Not Match

Imagine you are reviewing a teammate's work in Scala when you are confronted with the following piece of code. What would you think?

if (expr == true) {
return true;
} else {
return false;

Cast the first semicolon who never saw (or maybe even wrote) similar code. While it works and is strictly speaking correct, the code above disappoints for expressing what it does a little too literally. It is unsophisticated and ingenuous, a neophyte code smell almost. Experienced developers probably prefer the more compact idiom:

return expr;

Yet, it is very common to see even seasoned Scala developers decomposing Option via pattern matching:

def f[A](opt: Option[A]) = opt match {
case Some(a) => ???
case None => ???

Pattern matching on Option is almost never necessary as usually there are cleaner and more expressive ways to attain the same results with monadic functions. This is what the Option Scaladoc has to say:

The most idiomatic way to use an scala.Option instance is to treat it as a collection or monad and use mapflatMapfilter, or foreach [...] A less-idiomatic way to use scala.Option values is via pattern matching

Any expression of the form

opt match {
case Some(a) => foo(a)
case None => bar

is precisely equivalent to

opt map foo getOrElse bar

Furthermore, since Scala 2.10 an even more concise alternative is available, fold:


fold even gives us the additional benefit of being more type-strict than either of the previous alternatives. For instance, while a and b in the example below have a type of Any (with value 2), c fails to compile with a type mismatch error:

val opt = Option(1)

val a = opt match {
case Some(x) => x + 1
case None => "a"

val b = opt map (_ + 1) getOrElse "a"

val c = opt.fold("a")(_ + 1)

On the not-so-bright side, fold may not be as readable as the alternatives and a little error-prone, too, since it is not clear what comes first, None or Some?

Readable or not, we still can do a lot better than foldOption offers a few dozen methods, many of which can be used to better express more specific transformations. For example, opt.fold(true)(_ => false) is equivalent to opt.isEmpty. The converse is either opt.isDefined or its alias opt.nonEmpty.

The Ultimate Scala Option Cheat Sheet

We reproduce below a table highlighting the most relevant Option methods. Whenever you feel like writing case Some(a) => foo; case None => bar consult this table for the equivalent foo and bar in the Some(a) and None columns to find its corresponding method. It is also recommended that you refer to the Option Scaladoc for detailed descriptions and additional methods.

First, a few definitions. For arbitrary types A and B, let:

val a: A
def p(a: A): Boolean // a predicate
def f(a: A): B // a mapping function
def g(a: A): Option[B] // an optional mapping function

Method Meaning Some(a) None
map(f) Applies f to its content Some(f(a)) None
getOrElse(default) Retrieves its content or a default value a default
fold(default)(f) (2.10) Same as map f getOrElse default f(a) default
isEmpty Is it a None? false true
isDefined Is it a Some? true false
nonEmpty (2.10) Same as isDefined true false
size Same as isDefined, but returning 0 or 1 1 0
flatMap(g) Same as map, except g returns Option g(a) None
orElse(Option(default)) Same as getOrElse, but inside an Option Some(a) Option(default)
orNull For legacy code that expects null a null
filter(p) Turns a Some into None if its content does not conform to p Some(a) if p(a), otherwise None None
find(p) Same as filter Some(a) if p(a), otherwise None None
filterNot(p) (2.9) Same as filter(!p) Some(a) if !p(a), otherwise None None
contains(b) (2.11) Whether its content is equal to b a == b false
exists(p) Does its content conform to p? p(a) false
forall(p) (2.10) Same as exists, except None returns true (see note below) p(a) true
count(p) Same as exists, but returning 0 or 1 1 if p(a), otherwise 0 0
foreach(f) Applies side-effectful f to its content Unit (calls f(a)) Unit
flatten (2.10) “Unnest” a nested Option. Compile error if a itself is not an Option a None
toRight(default) Converts to Either Right(a) Left(default)
toLeft(default) Converts to Either Left(a) Right(default)
toList Converts to List List(a) Nil
toSeq Same as toList List(a) Nil
toSet Converts to Set Set(a) Set()
get Do not bother about what this is supposed to do, never use it :–) a NoSuchElementException

.exists and .forall

The most attentive readers may be baffled by why None.forall returns true.

If you have your favorite predicate logic introductory book at hand, you may want to read the sections on "universal quantification" and "vacuous truth". If not, trust us, the math checks out :-)

Here is a colloquial explanation. Basically, it boils down to forall being the dual of exists. This is easier to understand with an example:

def isPrime(n: Int): Boolean = ???

val a = Seq(2, 3, 5, 7)

val b = Seq(1, 2, 4, 8)

We can clearly see that a.forall(isPrime) is true, while a.forall(!isPrime) is false. However, for list b, both b.forall(isPrime) and b.forall(!isPrime) are false. Negating the predicate does not necessarily negates the result. The negation of forall(isPrime) is not forall(!isPrime), but exists(!isPrime). In other words:

forall(isPrime) == !exists(!isPrime)

It makes perfect sense in plain English: to say "all numbers are prime" is equivalent to saying "there does not exist a number that is not prime".

Back to OptionNone.exists(p) is always false, as is None.exists(!p). Therefore, by the identity derived above:

None.forall(p) == !None.exists(!p)
== !false
== true

As unintuitive and surprising as this result may be, it is true and correct, proven by much more rigorous mathematical derivation. As a matter of fact, this apparent oddity can be put to some pretty good use, as we will see next.

A more elaborate example

Let us start with a simple method: tracking events on a webpage. The only distinctive characteristic here is that whether we have a user logged in to the site, we record their ID.

def track(user: Option[User]) = user match {
case Some(u) => Tracker.track(
case None => Tracker.track(GUEST)

Now, due to privacy concerns, we have to allow users to opt out of tracking. Easy-peasy:

def track(user: Option[User]) = user match {
case Some(u) if u.canTrack => Tracker.track(
case _ => Tracker.track(GUEST)

All was going well, until one day we got a call from our lawyers: if a user opts out, they should never be tracked at all, not even anonymously. Hence, our final version:

def track(user: Option[User]) = user match {
case Some(u) if u.canTrack => Tracker.track(
case None => Tracker.track(GUEST)
case _ => // do not track

Now let us convert everything to not use pattern matching. The first one is trivial:

Tracker.track(user map ( getOrElse GUEST)

Not only did we replace the match with a more compact single-line expression, we do not have to write Tracker.track twice anymore. It may seem inconsequential, but over time such small things tend to grow in complexity, making maintenance harder.

The second version is more interesting. Whenever we see pattern guards, filter may be required. Overall, it does not change much from above:

Tracker.track(user filter (_.canTrack) map ( getOrElse GUEST)

Finally, something more interesting. Now we have not two, but three case clauses. Clearly, there is no single method in Option that handles three possible outcomes. The answer, however, is surprisingly simple once you know it:

if (user forall (_.canTrack))
Tracker.track(user map ( getOrElse GUEST)

Knowing that None.forall is always true was crucial to allow us to come up with a simple expression that handles all cases.

Refactoring and the M-word

It is important not to forget that Option is a monad, the Maybe monad, and as such can be used in for-comprehensions:

case class User(name: String, contactInformation: Option[ContactInformation])
object User {
def byId(id: Int): Option[User] = ???
case class ContactInformation(email: Option[Email])
case class Email(address: String, verified: Boolean)

def emailInformation(id: Int) = for {
user <- User.byId(id)
contactInformation <- user.contactInformation
email <- if email.verified
} yield (, email.address)

Finally, higher-order functions and for-comprehensions offer another advantage over pattern matching that comes in handy when refactoring code. It is not uncommon for methods or data structures of type Option[T] to be revamped to something like Seq[T]. While code using pattern matching would have to be rewritten, code using only higher-order functions or for-comprehensions may not require any changes at all. Most higher-order function and for-comprehension idioms are consistent across all collection types.

Tip: Prefer Option(a) over Some(a)

You may have noticed in some examples that we wrote val a = Option(1) instead of val b = Some(1). Usually, one should prefer Option(x) over Some(x). First, a is inferred as type Option[Int], while b is Some[Int]. Secondly, and more importantly, is that Option(x) is more type-safe than Some(x). Again, let us see why with an example:

// We are enthusiastic about newcomers
def hello(name: Option[String]) = s"Hello, ${name getOrElse "stranger" toUpperCase}!"

def a(name: String) = hello(Option(name))

def b(name: String) = hello(Some(name))

Which is better: method a or method b? Call a(null) and b(null) and see the answer for yourself.


All Option methods in the Scala standard library are implemented as single line methods that are slight variations of the form:

if (isEmpty) bar else foo

Most methods are also marked as @inline final, which means that using Option higher-order functions should be as efficient as safe-guarding for null in Java, especially after HotSpot does its magic. How cool is that?

Addendum: Do not abuse pattern matching

Pattern matching is a very expressive and powerful tool. However, as frequently is the case with the finest hammers, we must watch out for the proverbial nail.

Pattern match all the things

Pattern matching "all the things" must be considered a Scala code smell. Especially for some common cases, pattern matching is not always as readable or concise as other constructs, and often the bytecode generated by the Scala compiler is less efficient, too.

In particular, do not pattern match on Booleans; use good old, true and tested if else:

cond match {
case true => Ok
case false => Error

if (cond) Ok else Error

Avoid pattern matching on a single value followed by a defaut case:

n match {
case 0 => "Cart is empty"
case _ => "View cart"

if (n > 0) "View cart" else "Cart is empty"

s"Your cart has ${if (n > 0) n else "no"} item(s)."

Finally, for most data structures like Seq and String, consider methods like isEmptyheadOptiontail, etc. And for enumerations, use withName.

Enjoy your Options!