Category Theory

Handling exceptions in Scala with monad transformers

A very common characteristic of Scala code is a large number of for expressions (also known as for comprehensions). And for a good reason: for expressions are straightforward to read and relatively easy to write as they usually boil down to only two methods - map and flatMap.

For example, this is a very typical Scala code that first grabs the ID of a user from one server, and then uses this ID to query additional data from another endpoint.

for {
  user <- getCurrentUserData()
  employment <- getUserEmployment(user.id)
} yield UserEmploymentInfo(user.name, employment.employer)

This code is so simple that even a person unfamiliar with programming (let alone Scala) will immediately guess what's going on here.

However, there is an important assumption that the above code makes. In order for "for expressions" to work, both functions must return monadic-like structures (you can read about it in detail here), which basically means that these structures must have correctly-defined methods `map` and `flatMap`. This is usually not a problem in Scala, as many useful types such as `Option`, `Try`, `Either`, `Future` have these methods.

However, what will happen if the result type of a little more complicated, say, Future[Try[_]]? In this case, the simple "for expression" will not work as it can only extract the outer-most value.

It turns out, however, that this use case is so common and there are generic solutions as well. Several functional Scala libraries such Cats and ScalaZ expose these solutions as so-called monad transformers.

However, if you're only using monadic structures from the standard library, there is an easier way - a small library called Scala Hamsters:

val result = for {
  user <- FutureTry(getCurrentUserData())
  employment <- FutureTry(getUserEmployment(user.id))
} yield UserEmploymentInfo(user.name, employment.employer)
result.wrapped

The only noticeable difference is that now end up with an instance of `FutureTry` and therefore, we must unwrap it at the very end.