Paterson, Ross

2018-10-29 16:40:28 UTC

Reply

PermalinkI'm unhappy about the current state of Control.Monad.Error; the module

Use "Control.Monad.Except" instead

However, Control.Monad.Except implements 'fail' as an error, inheriting

the behavior of Identity. To my mind the whole point of

Control.Monad.Error is that it replicates the old non-error behavior of

`fail` that the Either type and EitherT used to have.

I think that fail on Either or EitherT has always been the default (error).Use "Control.Monad.Except" instead

However, Control.Monad.Except implements 'fail' as an error, inheriting

the behavior of Identity. To my mind the whole point of

Control.Monad.Error is that it replicates the old non-error behavior of

`fail` that the Either type and EitherT used to have.

The situation is similar with the MonadFail class; there is are

instance (Monad m, Error e) => MonadFail (ErrorT e m)

and

instance MonadFail m => MonadFail (ExceptT e m)

which means that Except doesn't have a MonadFail instance,

because Identity has none, whereas ErrorT implements a non-error

version of `fail`.

The principal flaw of ErrorT is that the monad methods had constraintsinstance (Monad m, Error e) => MonadFail (ErrorT e m)

and

instance MonadFail m => MonadFail (ExceptT e m)

which means that Except doesn't have a MonadFail instance,

because Identity has none, whereas ErrorT implements a non-error

version of `fail`.

on the exception type, even though they don't need them. (A lesser

issue was that the naming was inconsistent with the other transformers.)

One possibility would be to change the MonadFail instance for ExceptT

to give the functionality of the ErrorT instance:

instance (Monad m, Message e) => MonadFail (ExceptT e m)

(If it is decided in the other thread to have a non-default MonadFail

instance for Either, it would make sense to use that class here.)

Then, for base >= 4.9:

- there would be a MonadFail instance for ExceptT ... Identity

- the MonadFail instances for monads based on [], Maybe and IO would have

the constraint on the exception type, and different behaviour.

That would be awkward, but seems preferable to having two very similar

but slightly different transformers.