Closed jason-johnson closed 8 years ago
MonadError
implies two things:
1.) The type of error is determined by the monad.
2.) You can catchError
whatever it is that you throw with throwError
.
You can't just make instance MonadError e Maybe
because of the functional dependency.
Neither Maybe
nor []
can comply with the MonadError
invariants unless the 'e' in MonadError
is fixed to something like ()
.
I have no particular objection to adding this very restricted instance for each, but note as described above it can't be "whatever error type you want to throw", it can only be ()
.
Now, we can definitely comply with the laws with Maybe
in that restricted setting:
instance MonadError () Maybe where
throwError _ = Nothing
catchError m f = m <|> f ()
But in the case of []
, things get more subtle.
instance MonadError () [] where
throwError _ = []
is fine, but
catchError m f = m <|> f ()
is wrong and
catchError [] f = f ()
catchError m _ = m
is at least slightly dubious. It doesn't catch every time we throw, it only catches when all the non-deterministic branches "throw", although one could argue that all uses of non-determinism are implicitly using (<|>)
to "handle" nested failures and that that complies with MonadError
semantics.
Given that you will only be able to "throw" () in this scenario, it will likely be of limited utility to those who want to use this for mocking and the like, but they are legal.
On the other-other hand, these instances are inconsistent with the behavior of MaybeT
and ListT
.
Deciding not to proceed here because of the incompatibility with the existing MaybeT
and ListT
instances.
Sorry what's the incompatibility/inconsistency with MaybeT
?
The instance for MaybeT lifts the MonadError instance for the underlying monad.
instance MonadError e m => MonadError e (MaybeT m)
Having Maybe
and MaybeT Identity
have different semantics would be very awkward.
Ah of course, thanks. Maybe it's just best to add a catchM
to transformers
then.
There is a MonadError instance defined for Either, but neither Maybe nor List ([]) even though the transformer versions of those types are defined.
As it stands, users who may wish to treat a failure as a Nothing or empty list must define Orphaned instances.