Thursday, April 23, 2015

Scala Functional Toolkit: Handling exceptions in for comprehensions


As we develop more and more code in Scala, we are trying to stick to a few key principles. One of these is to avoid throwing Exceptions at all costs. In pretty much every case, this can be avoided by using Options or Eithers, however, sometimes it gets a little messy.

One such example we came across recently is when you are dealing with Futures, in particular doing something like a web request.

For example: Let's say you want to request some data from an API using Play's WebService library (or any HTTP library). Once you've got the result back you want to check whether the response was  a 200 or not, if so, you want to parse the JSON body. Finally, this all should be happening in side a Future so that it doesn't block your code.

In this case, the initial Future may succeed, but contain a 400, in which case you still want to fail.

Here is a naive implementation:

This is the kind of thing we don't want to do. Regardless of how it is handled by the caller, we'd prefer not to throw Exceptions anywhere in our code.

So we can get around this using Future.failed, however, as we are failing the future once the response has been received (that is the initial request future has now completed) we need to use flatMap instead of map. Otherwise our failure case would be Future[Future[JsValue]] and our success case would be Future[JsValue].

Ok. This is better. We are no longer throwing exceptions. We are now using the Future.failed method instead. However, it is a bit clunky. We can make this a slightly nicer using a for comprehension, however to do this we need to create a predicate method:

This method takes a boolean condition and then depending on the result either evaluates the success block, or returns a failed future with the exception specified. Using this we can now re-write the function as follows:

You can play around with this a little to create more versatile or specific variants. Whilst in the end this is all syntactic sugar, it has been a useful in making our code more concise.

No comments: