IntroWe're using Twitter's Finagle stack in my current scala project. Finagle is Future based and the Future concept makes usage of exceptions easy. Using Exceptions for "infrastructural" problems (like when the network or the database is down) is perfectly ok, in Java you would use RuntimeExceptions for this which don't have to show up in the method signature. In Scala there's no difference any more between checked exceptions and runtime exceptions. In Java checked exceptions are often used for "business" exceptions, i.e. exceptions that steer the way your business logic works. For example, some months ago in Java projects I've often written API methods like this:
So the possible return types of this method is either a ArticleDetails object containing the information about the article or an Exception saying that the article could not be found.
- Method signature tells you everything about the return types
- Two different ways of returning information
- return new ArticleDetails(...)
- throw new ArticleNotFoundException(...)
- This also means two different styles of handling this information at the caller side
ScalaIn Scala there are no checked exceptions any more, so migrating the above java interface to Scala and still use an exception for the failure case would result in the following:
The method signature doesn't tell you anymore that the method could produce a separate exception output. You could use scaladoc or @throws to document this, but the compiler doesn't check this and this probably leads (after some evolution cycles of your class) to a method signature that doesn't document anymore what it is returning in case of expected business flow failures.
Either to the rescueIn Scala there's a more functional way to say what the method is returning: Either
The above method either returns an ArticleNotFound or an ArticleDetails object.
An Either has a left side, which is used per convention for the failure case or a right side, which represents the "right", the success case.
Using FuturesAs I said we're using Twitter Futures for our code, so if we're doing some blocking method call, we do it async and return a Future[RETURN_OBJECT] instead of the simple method return object to keep the current thread from being blocked. So the above method blocks because of a call to MySQL and would therefore look like this:
For ComprehensionsSo far everything looks good, and it seems pretty easy to get away from business exceptions. But imagine now a second method which returns an articleId for an unique article name:
If someone has only the unique name and wants to retrieve the article details I would naively use the following for comprehension and would expect Left's (Failures) to fail fast and "bubble" out of the for comprehension:
Unfortunately the above doesn't compile. What we need is a way to compose the Future with Either. At this point I was very lost and asked for help in this stackoverflow question. Fortunately the help came at the Scala User Group Munich where Lars Hupel helped me a lot with this.
Monad Transformers or "My way to scalaz"The way out of this problem is a Monad transformer. A simple mental model for a Monad in Scala is a class which implements map, flatMap and withFilter and can therefore be used in a for comprehension.
Either and Futures are Monads and Monads don't compose out of the box together. There's even not a guarentee that mondas can be combined.
My first problem was now that scala.util.Either is not even a Monad. Only it's right or left protection is a Monad! Which makes it a little bit cumbersome to use. You'll have to say always .right in for comprehensions.
As I found out that scalaz has also an Either called \/ which is right biased and not neutral like scala.util.Either. The best: It has a transformer class EitherT to combine it with other Monads.
Converting the above example to \/ leads us to:
Pretty similar to the solution with Scala's Either, but without .right's, because of it's right biasing like in Haskell. To use EitherT with Twitter Futures we now only need to define the following implicit vals:
Then we can use EitherT together with Twitter Futures in our for comprehension:
The run method turns the resulting EitherT back to a Future[\/]. A EitherT in this case is an EitherT[Future, ArticleNotFound.type, ArticleDetails], so this could be used as alternate return type. Then you can omit the run call.
Here's the final complete example:
If a left happens on one of the calls in the for comprehension, it bubbles out in fail fast strategy. If there are only Right's than the article is yielded.