For the last couple of weeks, we have pressed ahead and covered a lot of ground concerning some rather advanced techniques, particularly ones related to pattern matching and extractors. Time to shift down a gear and look at one of the more fundamental idiosyncrasies of Scala: the
If you have participated in the Scala course at Coursera, you have already received a brief introduction to this type and seen it in use in the
Map API. In this series, we have also used it when implementing our own extractors.
And yet, there is still a lot left to be explained about it. You may have wondered what all the fuss is about, what is so much better about options than other ways of dealing with absent values. You might also be at a loss how to actually work with the
Option type in your own code. The goal of this part of the series is to do away with all these question marks and teach you all you really need to know about
Option as an aspiring Scala novice.
The basic idea
If you have worked with Java at all in the past, it is very likely that you have come across a
NullPointerException at some time (other languages will throw similarly named errors in such a case). Usually this happens because some method returns
null when you were not expecting it and thus not dealing with that possibility in your client code. A value of
null is often abused to represent an absent optional value.
Some languages treat
null values in a special way or allow you to work safely with values that might be
null. For instance, Groovy has the null-safe operator for accessing properties, so that
foo?.bar?.baz will not throw an exception if either
foo or its
bar property is
null, instead directly returning
null. However, you are screwed if you forget to use this operator, and nothing forces you to do so.
Clojure basically treats its
nil value like an empty thing, i.e. like an empty list if accessed like a list, or like an empty map if accessed like a map. This means that the
nil value is bubbling up the call hierarchy. Very often this is okay, but sometimes this just leads to an exception much higher in the call hierchary, where some piece of code isn’t that nil-friendly after all.
Scala tries to solve the problem by getting rid of
null values altogether and providing its own type for representing optional values, i.e. values that may be present or not: the
Option[A] is a container for an optional value of type
A. If the value of type
A is present, the
Option[A] is an instance of
Some[A], containing the present value of type
A. If the value is absent, the
Option[A] is the object
By stating that a value may or may not be present on the type level, you and any other developers who work with your code are forced by the compiler to deal with this possibility. There is no way you may accidentally rely on the presence of a value that is really optional.
Option is mandatory! Do not use
null to denote that an optional value is absent.
Creating an option
Usually, you can simply create an
Option[A] for a present value by directly instantiating the
Some case class:
Or, if you know that the value is absent, you simply assign or return the
However, time and again you will need to interoperate with Java libraries or code in other JVM languages that happily make use of
null to denote absent values. For this reason, the
Option companion object provides a factory method that creates
None if the given parameter is
null, otherwise the parameter wrapped in a
Working with optional values
This is all pretty neat, but how do you actually work with optional values? It’s time for an example. Let’s do something boring, so we can focus on the important stuff.
Imagine you are working for one of those hipsterrific startups, and one of the first things you need to implement is a repository of users. We need to be able to find a user by their unique id. Sometimes, requests come in with bogus ids. This calls for a return type of
Option[User] for our finder method. A dummy implementation of our user repository might look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13
Now, if you received an instance of
Option[User] from the
UserRepository and need to do something with it, how do you do that?
One way would be to check if a value is present by means of the
isDefined method of your option, and, if that is the case, get that value via its
1 2 3 4
This is very similar to how the
Optional type in the Guava library is used in Java. If you think this is clunky and expect something more elegant from Scala, you’re on the right track. More importantly, if you use
get, you might forget about checking with
isDefined before, leading to an exception at runtime, so you haven’t gained a lot over using
You should stay away from this way of accessing options whenever possible!
Providing a default value
Very often, you want to work with a fallback or default value in case an optional value is absent. This use case is covered pretty well by the
getOrElse method defined on
Please note that the default value you can specify as a parameter to the
getOrElse method is a by-name parameter, which means that it is only evaluated if the option on which you invoke
getOrElse is indeed
None. Hence, there is no need to worry if creating the default value is costly for some reason or another – this will only happen if the default value is actually required.
Some is a case class, so it is perfectly possible to use it in a pattern, be it in a regular pattern matching expression or in some other place where patterns are allowed. Let’s rewrite the example above using pattern matching:
1 2 3 4 5
Or, if you want to remove the duplicated
println statement and make use of the fact that you are working with a pattern matching expression:
1 2 3 4 5 6
You will hopefully have noticed that pattern matching on an
Option instance is rather verbose, which is also why it is usually not idiomatic to process options this way. So, even if you are all excited about pattern matching, try to use the alternatives when working with options.
There is one quite elegant way of using patterns with options, which you will learn about in the section on for comprehensions, below.
Options can be viewed as collections
So far you haven’t seen a lot of elegant or idiomatic ways of working with options. We are coming to that now.
I already mentioned that
Option[A] is a container for a value of type
A. More precisely, you may think of it as some kind of collection – some special snowflake of a collection that contains either zero elements or exactly one element of type
A. This is a very powerful idea!
Even though on the type level,
Option is not a collection type in Scala, options come with all the goodness you have come to appreciate about Scala collections like
Set etc – and if you really need to, you can even transform an option into a
List, for instance.
So what does this allow you to do?
Performing a side-effect if a value is present
If you need to perform some side-effect only if a specific optional value is present, the
foreach method you know from Scala’s collections comes in handy:
The function passed to
foreach will be called exactly once, if the
Option is a
Some, or never, if it is
Mapping an option
The really good thing about options behaving like a collection is that you can work with them in a very functional way, and the way you do that is exactly the same as for lists, sets etc.
Just as you can map a
List[A] to a
List[B], you can map an
Option[A] to an
Option[B]. This means that if your instance of
Option[A] is defined, i.e. it is
Some[A], the result is
Some[B], otherwise it is
If you compare
None is the equivalent of an empty list: when you map an empty
List[A], you get an empty
List[B], and when you map an
Option[A] that is
None, you get an
Option[B] that is
Let’s get the age of an optional user:
flatMap and options
Let’s do the same for the gender:
The type of the resulting
Option[Option[String]]. Why is that?
Think of it like this: You have an
Option container for a
User, and inside that container you are mapping the
User instance to an
Option[String], since that is the type of the
gender property on our
These nested options are a nuisance? Why, no problem, like all collections,
Option also provides a
flatMap method. Just like you can
List[List[A]] to a
List[B], you can do the same for an
1 2 3
The result type is now
Option[String]. If the user is defined and its gender is defined, we get it as a flattened
Some. If either the use or its gender is undefined, we get a
To understand how this works, let’s have a look at what happens when flat mapping a list of lists of strings, always keeping in mind that an
Option is just a collection, too, like a
1 2 3 4 5 6
If we use
flatMap, the mapped elements of the inner lists are converted into a single flat list of strings. Obviously, nothing will remain of any empty inner lists.
To lead us back to the
Option type, consider what happens if you map a list of options of strings:
1 2 3
If you just map over the list of options, the result type stays
flatMap, all elements of the inner collections are put into a flat list: The one element of any
Some[String] in the original list is unwrapped and put into the result list, whereas any
None value in the original list does not contain any element to be unwrapped. Hence,
None values are effectively filtered out.
With this in mind, have a look again at what
flatMap does on the
Filtering an option
You can filter an option just like you can filter a list. If the instance of
Option[A] is defined, i.e. it is a
Some[A], and the predicate passed to
true for the wrapped value of type
Some instance is returned. If the
Option instance is already
None or the predicate returns
false for the value inside the
Some, the result is
1 2 3
Now that you know that an
Option can be treated as a collection and provides
filter and other methods you know from collections, you will probably already suspect that options can be used in for comprehensions. Often, this is the most readable way of working with options, especially if you have to chain a lot of
filter invocations. If it’s just a single
map, that may often be preferrable, as it is a little less verbose.
If we want to get the gender for a single user, we can apply the following for comprehension:
1 2 3 4
As you may know from working with lists, this is equivalent to nested invocations of
flatMap. If the
UserRepository already returns
None or the
None, the result of the for comprehension is
None. For the user in the example, a gender is defined, so it is returned in a
If we wanted to retrieve the genders of all users that have specified it, we could iterate all users, and for each of them yield a gender, if it is defined:
1 2 3 4
Since we are effectively flat mapping, the result type is
List[String], and the resulting list is
gender is only defined for the first user.
Usage in the left side of a generator
Maybe you remember from part three of this series that the left side of a generator in a for comprehension is a pattern. This means that you can also patterns involving options in for comprehensions.
We could rewrite the previous example as follows:
1 2 3
Some pattern in the left side of a generator has the effect of removing all elements from the result collection for which the respective value is
Options can also be chained, which is a little similar to chaining partial functions. To do this, you call
orElse on an
Option instance, and pass in another
Option instance as a by-name parameter. If the former is
orElse returns the option passed to it, otherwise it returns the one on which it was called.
A good use case for this is finding a resource, when you have several different locations to search for it and an order of preference. In our example, we prefer the resource to be found in the config dir, so we call
orElse on it, passing in an alternative option:
1 2 3 4
This is usually a good fit if you want to chain more than just two options – if you simply want to provide a default value in case a given option is absent, the
getOrElse method may be a better idea.
In this article, I hope to have given you everything you need to know about the
Option type in order to use it for your benefit, to understand other people’s Scala code and write more readable, functional code. The most important insight to take away from this post is that there is a very basic idea that is common to lists, sets, maps, options, and, as you will see in a future post, other data types, and that there is a uniform way of using these types, which is both elegant and very powerful.
In the following part of this series I am going to deal with idiomatic, functional error handling in Scala.