Posted on March 26, 2017 by Amir Barylko

Exceptions should be exceptional

Error handling is a key part of any application.

However, using exceptions instead of a return type to express a function call can fail can be a bit confusing.

For example let’s take a look at java.lang.Integer.parseInt

public static int parseInt(String s) throws NumberFormatException

The signature of parseInt expresses clearly that it may fail by throwing a NumberFormatException.

So either we need to catch the exception and see if we can do something about it, or perhaps we could just let the exception bubble up to the next catch.

Being honest though, we do not want to let a NumberFormatException escape from our function because is meaningful to the function context but not outside.

We can not avoid dealing with the exception. Maybe we could return a new type that is a bit more helpful, like perhaps a Maybe<int> or PossibleResult<int> instance that would help us take a decision without involving try and catch.

Wait… why not null? Well, here’s why: Tony Hoare - The billion dollar mistake.

The Optional value

In Java 8 a new class was introduced to capture similar scenarios.

The Optional<T> represents a value that may or may not be there. It has functions like map that help to transform the possible result if present. Another useful function is orElse to obtain the value or provide an alternative.

With that in mind, parseInt could change to

public static Optional<Integer> parseInt(String s)

And we could use it to decide what to do in case it fails by chaining functions

public Integer getPortNumber(String strPort) {
  return parseInt(strPort).orElse(8080);
}

In Clojure is not idiomatic to use an Optional class, nor a Maybe type like Haskell.

However, there is a value chosen to represent Nothing and that value is nil.

The difference is that the and and or functions know how to work with it.

(defn get-port-number 
  [str-port]
  (or (parse-int str-port) 8080))

A guideline to consider

Use exceptions when an unexpected event happens that there is not coming back from it.

Run out of memory, file not found, network unreachable, etc.

Every function/method we write is establishing a contract with the user. If failure is a possible result then we should reflect that in the return type.

For example calling an HTTP API always can fail. In Clojure we can return a map with different keywords that represents the result.

When the call succeeds, then the :body keyword will be there, and when the call failed, an :error keyword will indicate what is the error.

Similar to Haskell’s Either data type in Java we have to be a bit more creative and have a class that represents success or failure.