Future and Either together
When handling errors in Scala the Either type is very useful since it allows us to define the type of our right result (the success) as well as the type of the left (failure) result.
Just a warning, I use Either and \/ (ScalaZ disjunction) interchangably in this post.
Often our functions are also expected to run concurrently using a Future. When we want to combine both capabilities together we end up with type signature like this one:
Both Either and Future are monads, which means that we can chain them together using a sequence of flatmap and map operations.
Let’s consider two rather contrived functions just for exploring how Future and Either work together.
As you can see it’s a bit messy to work with Future[Either] because at each step of the computation we need to reach into the Future with map, check the Either and then pass it on to the next step.
Usually when we see this staircase pattern you can utilize a for comprehension to simplify things.
Except we can’t do that because rb1 and rb2 are getting the result of the future but not inside the disjunction. And since you can’t have different effect types in a for comprehension (it has to play nicely with flatmap) we are stuck. We could extract the values from the futures in one for comprehension, then in a second one we could extract from the Eithers, but that has the problem that all of the futures have to run before our second for comprehension, and that means we could waste time completing one of the later futures when an earlier result is Left (failure) case.
Introducting EitherT. EitherT is a monad transformer, and appears in various libraries such as ScalaZ, Cats and Hamsters. For the Cats version of EitherT checkout this interesting blog post eed3si9n
For ScalaZ and Hamsters keep reading!
Using ScalaZ transformers we can write our code very similarly to the code above simply by wrapping each step in an eitherT constructor…
That’s very straightforward, and now you can see that we are able to reach into the Future result and the Either result at the same time. Behind the scenes we’re constructing the transformer which when flatmapped knows how to do the steps that we would have done manually.
The only complication here is that now our result type at the end is not Future[\/[String, String]] like we’d expect but in fact is EitherT[Future, String, String]
In order to get back to where we were ScalaZ provids a run function. So the full example looks like this:
There is one further complication with this. In order to transform to EitherT we need a Monad[Future] otherwise we’ll get a compile error as follows.
You don’t get one for free (no pun intended) in Scalaz so let’s define one as follows
There’s a little bit of extra work going on here, I allow the Monad[Future] to be constructed from an execution context. The reason for that is you need to know which execution context your future is running in. By making the class this way I’m able to pick up an execution context implicitly defined in the same scope.
See this stackoverflow question (I asked it!) for more detail on this.
Here’s a scala fiddle to demonstrate all this working:
If you don’t want to bring in a big library like ScalaZ just for this feature, there is a nice micro library called Hamsters which contains some useful utilities, one of them being FutureEither.
Using FutureEither mirrors our approach above almost exactly. The difference is we don’t need to jump through hoops to make our own Monad[Future] and instead of a ‘run’ function, hamsters has a function ‘future’ which turns the FutureEither back into a Future[Either]
The other difference is that we’re required to use the built in Scala Either instead of ScalaZ’s disjuction.
Hamsters has the advantage that the source code is a lot easier to read than that of Scalaz. Take a look! MonadTransformers.scala
You can also take advantage of an implicit conversion to get rid of the need for calling ‘future’ at the end. Note that I added a type annotation when setting r which will make Scala look for the impclicit conversion.
Just for reference the libraries used when writing this post are as follow: