Error handling in Scala With Play! Framework

The problem

If you come from the Java world and use try/catch to handle exceptions, you may have experience a few troubles in Scala. According to functionnal paradygme, classical try / catch must be avoid as it is a side effect. To replace that Scala has his own tools :

  • Option allow to handle simple cases, mainly “does my data exist ?”, e.g. load a user from DB with id = 12. It’s a good tool to avoid NullPointerException
  • Try allow to handle exception with stacktrace, e.g. wrap a Java lib in scala
  • Either allow to handle error with custom object (usually a beautiful String). By convention the right side is the data and the left side is the error.

In tutorials it looks shinny but as soon as we dive in a real application, we use asynchronous call, json parsing, etc… and we end with type like Future[Either[String, User]] and crazy folding begins.

The main issue is the code looks really ugly. If you don’t write it yourself it’s a pain to understand such code. Where is the beautiful Scala syntax that was sold to us ???

Another problem is the error case may be handle far away from the success case. With the first fold, we have 10 lines between the success and error cases … and it’s a simple exemple.

I’ll try to explain how we solve this at Particeep with some monadic stuff. 
We use intensively Play! so every exemple will be base on the Play framework but it may be translate to every project quickly. The code is available on github

First Solution : Monadic Action

After the first controller written like above, I start looking for an alternative. Happily the good folk of Kanaka-io did “MonadicAction” (their github). If you speak french, their slides will explain clearly how it works. For the rest of you (yes I speak french ^^) I’ll try a quick explanation :

A pattern emerge in our code : we use Future[Option[A]] or Future[Either[A, B]] on every DAO return. We get our result in the future and it may miss or return an error. To avoid the double map (first on future and second on Option/Either) we need a new operator. Scalaz has FutureT that do the job. Basically it allow you to do that :

It compile !

  • We handle error close to the source of the error
  • It’s still a little bit complexe to read and it add boilerplate (eg: result.run.merge)

MonadicAction give us a mini-DSL that allow us to rewrite the updateStatus like that

It look really nice and clear aka no boilerplate

  • Errors are handle as close as possible of the code that generate them and not at the end of flatMaps
  • The operator ?| handle a lot of commons cases (Future[Option], Future[Either], Json error, etc…)
  • We have implicit converters from EitherT[Result, A] to Future[Result\/Result] to Future[Result]

At that point we manage to write nice controllers in Play! However Monadic Action only works in controller because it needs to return a “Result”. When your project will grow, you’ll have multiple layers doing their own job (models, services, wrapper on external libs, you name it…) The same use case will appear on every layer but they don’t return “Result”. We need to generalize Monadic Action from Future[Result\/Result] to Future[ErrorStructure\/A]

If you’re not familiar with scalaz, \/ is like Either but it’s “right biased” i.e. if you map on it you map on the right side : the “success side” by convention on Either. Just like Option map on the Some side by default. It’s also called disjunction. Last thing : A \/ B is equivalent to \/[A, B]

What is a good ErrorStructure ?

I mention ErrorStructure but that’s the sake of the discussion.

At first we start with a simple String that carry the error message but soon this was not enough : if the String carry the technical error (the one we need to debug and solve problem) then the controllers is in charge of converting technical error to human error. That feels wrong as it’s not the controller’s job. We can use a tuple but if we want to carry the Exception to it become a tuple3 … at that point we better define our own data structure

I introduce our new structure for error : Fail

It’s greatly inspire from this presentation

Fail allow us to :

  • Handle simple error message like String or any Throwable
  • Chain Fail to keep the hierarchy of errors
  • We stick with immutability
  • We keep it simple to use
try{

}catch { case err:Exception => Fail(“can’t query user from DB”).withEx(err) }

Now that we have our error structure we can build our operator that produce a Future[Fail, A]

Second Solution : Sorus

Sorus : don’t google it, I write it so I name it ! When I start functional programming I often see word that has no meaning (at least without reading dozen of wikipedia pages about Mathematical theory) like monadic, functor or Kleisli. So why not add a new one, just for my personnal pleasure :D

Sorus is the generalization of Monadic Action.

Now that we got Fail we change MonadicAction to handle EitherT[Future, Fail, A] instead of EitherT[Future, Result, A]. This allow us to use it everywhere in our application !

If you follow the type EitherT[Future, Fail, A] will convert to Future[Fail \/ A]

We don’t even write Fail because the operator handle it for us. If the expression before the operator generate a Throwable, it wrap it into a Fail with our error message. If the expression generate another kind of error (Either.Left, Option.None, etc…) it wrap the error message in a Fail and our new error message.

A more complex exemple

Use case : we want to use Amazon API to retrieve a list of books

If parseXml throw an Exception, we get the following result :

error.amazons.parse.xml <- test exception

If buildRequestUrl throw an Exception, we get the following result :

error.amazons.search.request <- error.amazons.build.request.url <- test exception

This give us flexibility :

  • we get access to the root exception
  • we can follow error path through our method according that we use good error messages

Conclusion

It’s really a quick tour about what we can do to improve error handling in Scala. But at least we improve the syntax of for comprehension and error handling

The next step would be to transform Fail to Result automatically according to a convention. Our application will always respond in a coherent way when an error occurs. It’s a very useful behavior if you’re building an API.

If you see improvements or if you have any questions don’t hesitate to reach me, i’ll be happy to help you