I'm trying to get my head around error handling in Haskell. I've found the article "8 ways to report errors in Haskell" but I'm confused as to why Maybe and Either behave differently.
For example:
import Control.Monad.Error
myDiv :: (Monad m) => Float -> Float -> m Float
myDiv x 0 = fail "My divison by zero"
myDiv x y = return (x / y)
testMyDiv1 :: Float -> Float -> String
testMyDiv1 x y =
case myDiv x y of
Left e -> e
Right r -> show r
testMyDiv2 :: Float -> Float -> String
testMyDiv2 x y =
case myDiv x y of
Nothing -> "An error"
Just r -> show r
Calling testMyDiv2 1 0
gives a result of "An error"
, but calling testMyDiv1 1 0
gives:
"*** Exception: My divison by zero
(Note the lack of closing quote, indicating this isn't a string but an exception).
What gives?
A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
Strictly speaking, Either cannot be a monad, as it has kind Type -> Type -> Type ; Either String can be (and is), because it has kind Type -> Type .
Monad is a simple and powerful design pattern for function composition that helps us to solve very common IT problems such as input/output, exception handling, parsing, concurrency and other. Application becomes less error prone.
Lists are a fundamental part of Haskell, and we've used them extensively before getting to this chapter. The novel insight is that the list type is a monad too! As monads, lists are used to model nondeterministic computations which may return an arbitrary number of results.
The short answer is that the Monad class in Haskell adds the fail
operation to the original mathematical idea of monads, which makes it somewhat controversial how to make the Either type into a (Haskell) Monad
, because there are many ways to do it.
There are several implementations floating around that do different things. The 3 basic approaches that I'm aware of are:
fail = Left
. This seems to be what most people expect, but it actually can't be done in strict Haskell 98. The instance would have to be declared as instance Monad (Either String)
, which is not legal under H98 because it mentions a particular type for one of Either
s parameters (in GHC, the FlexibleInstances extension would cause the compiler to accept it).fail
, using the default implementation which just calls error
. This is what's happening in your example. This version has the advantage of being H98 compliant, but the disadvantage of being rather surprising to the user (with the surprise coming at runtime).fail
implementation calls some other class to convert a String into whatever type. This is done in MTL's Control.Monad.Error
module, which declares instance Error e => Monad (Either e)
. In this implementation, fail msg = Left (strMsg msg)
. This one is again legal H98, and again occasionally surprising to users because it introduces another type class. In contrast to the last example though, the surprise comes at compile time.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With