Functional Programming | Java | Programming | Ruby | ScalaKnow Your OptionsAugust 4, 2014
Imagine a method in Java that retrieves an
Employee entity from the database by the employee id. Something like this:
That's pretty straightforward. You've probably written hundreds of methods like this one. The problem is what to do if there is no employee with that id. Java developers usually consider two options for this scenario:
- Throw a checked exception
Sure, you could also apply the Null Object Pattern, but no one ever does that.
Throwing a checked exception has one huge advantage. It explicitly alerts you to the possibility you won't find an employee with the id, and the compiler forces you to deal with that possibility by either kicking the can down the road with a rethrow or by catching the exception. The problem is that using exceptions to regulate flow control is generally considered a bad idea.
On the other hand, returning
null implies using a conditional statement for flow control, which is preferred. The problem is that
is basically the worst thing ever. Even the guy who invented it
thinks so. For details on
null is basically the worst thing ever, check out what the Google Guava team
has to say about it.
Various languages address the issues with
null in different ways. Ruby, for example, has
nil, which is a singleton
instance of NilClass, which can be
monkey-patched in clever ways
to mitigate some of those issues.
A much cleaner, more sophisticated approach is to use the concept of optional types,
which is sort of utilized in Guava and has its origins in mathematical type theory
and functional programming languages like Haskell and Scala, where I was first introduced to the
concept. Since then, Scala's
Option type has become one of my favorite features of the language.
Let's go back to our original example. In Scala, it would probably look like this.
Check out that
Option[Employee] return type. The
Option type has a rigorous definition, but as a practical matter, think of it as a one-item
collection. If we find an employee with the given id, the collection contains the corresponding
Employee instance. If
we don't, the collection contains a singleton--similar to Ruby's
nil and some applications of the Null Object Pattern.
What's the big deal? Two things.
First, just like with the exception approach, we have a compile-time mandate to account for both possibilities. There is no chance we will be caught by surprise at runtime and slow down our development cycle. But this compile-time checking is accomplished with type safety rather than a problematic exception.
Second, and even better, the caller's code is really clean. You could call our method like this:
If you have worked with Scala collections before, you'll notice how similar it is to use
Option. In this case,
map call extracts the
Employee instance “inside” the
Some, and you can do whatever you want
with it. Here we get the employee's name in the
map call and run the name through a
filter to make
sure it isn't blank. If it isn't, the employee's name is stored in the variable. The
getOrElse call never happens.
filter calls never happen, and
getOrElse handles this alternate flow. In fact,
executes when any method along the call chain returns
None. So if there is an employee with given id but the name is blank,
getOrElse does its thing in this case as well.
All scenarios are accounted for, and the code is elegant and pretty easy to follow if you are comfortable with actual
collections. You can even use flatMap
if you have
Option's. That's cool.
Hopefully now you know why I love
Option so much. Even if you don't program in Scala, you can think differently
about your code no matter which language you use. For much more on
Option, check out Daniel Westheide's
on the topic.