(un-)deprecation of Control.Monad.Error

Dear list, I'm unhappy about the current state of Control.Monad.Error; the module is deprecated, stating: Module ‘Control.Monad.Error’ is deprecated: 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. To wit:
fail "123" :: ErrorT String Identity () ErrorT (Identity (Left "123"))
fail "123" :: Except String () ExceptT (Identity *** Exception: 123
In this light I find this deprecation message disingenuous. 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`. Can we undeprecate the Control.Monad.Error module? Cheers, Bertram

On Sat, Oct 27, 2018 at 03:53:48PM +0200, Bertram Felgenhauer via Libraries wrote:
I'm unhappy about the current state of Control.Monad.Error; the module is deprecated, stating:
Module ‘Control.Monad.Error’ is deprecated: 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).
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 constraints 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.

Paterson, Ross wrote:
I think that fail on Either or EitherT has always been the default (error).
By "old" I meant "really old". The instance was changed 8 years ago, when ghc-7.0.1 was released. You can find traces of it in Control.Monad.Error in mtl-1.*. mtl-2.* removed those traces. The last appearance was in mtl-1.1.1.1, conditional on the version of base: https://hackage.haskell.org/package/mtl-1.1.1.1/docs/src/Control-Monad-Error... Further back in history, mtl-1.0.0 was released with ghc-6.4.1, and contained the above instance unconditionally; before that, the instance was in base. I stopped looking at ghc-6.0.1. Cheers, Bertram

Paterson, Ross wrote:
The principal flaw of ErrorT is that the monad methods had constraints 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)
Indeed, with the MonadFail proposal coming to its conclusion that will be possible, and personally I'd be happy with that. However, I suspect that both possible MonadFail instances instance (Monad m, Message e) => MonadFail (ExceptT e m) instance (MonadFail m) => MonadFail (ExceptT e m) are actually useful, and then we need two types, even though it looks awkward. Cheers, Bertram
participants (2)
-
Bertram Felgenhauer
-
Paterson, Ross