1. 8
  1.  

  2. 2

    I have tried several approaches to modelling errors with effect types in Scala, and they all stink a little bit.

    The first one is the one the Bifunctor IO is a counterpoint to, which is using plain IO[A], which is MonadError[A, Throwable]. To represent my error states I use sum types that at the top extend from java.lang.Exception. This is practical, because if I’m writing a web server, I can verify if it was a known exception (like BusinessLogicRejection) or some other error. Most if not all of these ...Rejection types are recoverable and non-fatal, and produce a 4xx HTTP code. Any other Exception is most likely a 5xx error or a 4xx error.

    This approach stinks because I have to have an “open” model of errors: by extending Exception I have to deal with all Exceptions and distinguish the ones in my error hierarchy from other exceptions. On the other hand, this model is really, really easy to use since I can either IO.raiseError(IdempotencyBusinessBlahBlahRejection) or call some arcane JVM crap and that error gets handled seamlessly. But, at heart, it’s dynamically typed. Having dynamically typed errors gives me these sudden flashbacks, the stuff from nightmares where everything is on fire and you’re so, so alone, that I would probably sleep better when my errors are “closed” in the sense that everything I know is either recoverable, represented by my custom error ADT, or fatal, like ...Error in JVM lingo.

    The second approach is to either have IO[Either[E, A]] where E is the recoverable error (I often use the word rejection for these), but this is requires using monad transformers and they are cumbersome and have an inherent performance hit, because monad transformers don’t work nicely on the JVM. So while this is completely typed, it’s annoying to use, and slow. It stinks too!

    So the bifunctor IO essentially solves this problem by merging these two. On one hand, I am forced to have a “closed” error model, but I don’t have to use monad transformers. Woot!

    Time will tell if cats adopts this approach or whether they continue with the current Throwable approach. Seems like the cats-effect community is divided on this, exhibit A and exhibit B. It is certain that Scalaz 8 will have bifunctor IO, but that library isn’t released yet. Until then, this will be very interesting to watch!

    Thanks @jdegoes for all your work in improving the functional programming experience in Scalaz!