I understand (I think) that there is a close relationship between Either
and Except
in Haskell, and that it is easy to convert from one to the other. But I'm a bit confused about best practices for handling errors in Haskell and under what circumstances and scenarios I would choose one over the other. For example, in the example provided in Control.Monad.Except
, Either
is used in the definition
type LengthMonad = Either LengthError
so that calculateLength "abc"
is
Right 3
If instead one were to define
type LengthMonad = Except LengthError
then calculateLength "abc"
would be
ExceptT (Identity (Right 3))
I'm confused about what purpose this would serve and when one one want it. Why does everything returned from calculateLength
always have Identity
; why not just SomeExceptionType (Right 3)
or even SomeSuccessType 3
?
I'm a Haskell beginner when it comes to concepts like this, so a concrete example of when I'd want the latter over the former would be much appreciated, especially why it's so (apparently to me) complex. For example, what can a caller of a function that uses the Except
version of calculateLength
do, that they can't (or at least can't as easily) do with the Either
version?
Use Either
for normal success/error APIs. It's defined in the base library, so it doesn't push other dependencies on a consumer. Also, this is one of the most basic Haskell types, so 'everyone' understands how it works.
Only use ExceptT
if you specifically need to combine Either
with another monad (such as, for example IO
). This type is defined in the transformers library, so pushes an extra dependency on consumers. Additionally, monad transformers is a more advanced feature of Haskell, so you can't expect everyone to understand how to use it.
I wasn't around when those decisions were made, but it seems that there are various historical reasons for the confusion. Haskell is an old language (older than Java!), so even though efforts have been made to streamline it and rectify old mistakes, some still remain. As far as I can tell, the Either
/ExceptT
confusion is one of those situations.
I'm speculating that Either
is older than the concept of monad transformers, so I imagine that the type Either
was introduced to the base library early in the history of Haskell.
The same thing seems to be the case with Maybe
.
Other monads, likes e.g. Reader and State seem to have been introduced (or at least 'retconned') together with their monad transformers. For example, Reader
is just a special case of ReaderT
, where the 'other' Monad
is Identity
:
type Reader r = ReaderT r Identity
The same goes for StateT
:
type State s = StateT s Identity
That's the general pattern for many of the monads defined in the transformers library. ExceptT
just follows the pattern by defining Except
as the special case of ExceptT
.
There are exceptions to that pattern. For example, MaybeT
doesn't define Maybe
as a special case. Again, I believe that this is for historical reasons; Maybe
was probably around long before anyone started work on the transformers library.
The story about Either
seems even more convoluted. As far as I can tell, there was, originally, an EitherT
monad transformer, but apparently (I forget the details) there was something wrong with the way that it behaved (it probably broke some laws), so it was replaced with another transformer called ErrorT
, which again turned out to be wrong. Third time's the charm, I suppose, so ExceptT
was introduced.
The Control.Monad.Trans.Except
module follows the pattern of most other monad transformers by defining the 'uneffectful' special case using a type alias:
type Except e = ExceptT e Identity
I suppose it does that because it can, but it may be unfortunate, because it's confusing. There's definitely prior art that suggests that a monad transformer doesn't have to follow that pattern (e.g. MaybeT
), so I think it would have been better if the module hadn't done that, but it does, and that's where we are.
I would essentially ignore the Except
type and use Either
instead, but use ExceptT
if a transformer is required.
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