Exception in Haskell by Yuri Khan 2014-09-26

By Xah Lee. Date: .

Exception in Haskell

by Yuri Khan,

(Yuri Khan https://yurikhan.dreamwidth.org/)

OK, let's consider Haskell. The canonical textbook explanation of error handling in Haskell goes like this:

Functions that may not be defined for all possible inputs are called “partial functions”. The simplest way to represent a partial function is the Maybe resultType monad. A successful return with value x is represented as Just x, while an error is represented as Nothing. Monadic functions may be chained; if any function in the chain returns Nothing, the whole chain returns Nothing. (This provides the error autopropagation issue.)

myDivide :: Fractional a => a -> a -> Maybe a
myDivide _ 0 = Nothing
myDivide x y = x / y

But returning just one error return value is insufficient for real world programming; at a minimum, we'd like to return a human-readable errror description. Instead of Maybe resultType, we can return Either String resultType, where a Right x value represents a successful return, and a Left "something went wrong" represents an error and provides specific error details. Either a is also a monad and so a function f :: a -> Either e b can be chained with g :: b -> Either e c, to yield a composite function :: a -> Either e c. A chain of functions returns the earliest error, or if none occur, the successful result of the last function in chain.

But human-readable error messages are not very useful within programs; you could say we don't have the exception type tagging property. To this end, we can use Either with a custom error type instead of String, for example, Either (ErrorCode, String) resultType. This way, error objects are machine-readable; we can switch on the error code and decide which errors to handle and which to propagate. (For example, a function that uses a configuration file might query the configuration file for an option, and if this fails because the option is unconfigured, use a default value; but if it fails because the option value is syntactically incorrect, fail.)

But in order for this approach to work in large, the ErrorCode has to be the same throughout the program, which is hard to achieve if you use libraries. This is because the Either (ErrorCode, String) resultType mechanism lacks extensibility. For that reason, several exception support modules (Control.Exception, Control.Monad.Exception) are devised. I am not prepared to give a good analysis on those right now, but I see that they achieve extensibility by defining a typeclass that can be defined for any client-defined error type.