haskell / mtl

The Monad Transformer Library
http://www.haskell.org/haskellwiki/Monad_Transformers
Other
362 stars 63 forks source link

No MonadError instance for Maybe or [] #27

Closed jason-johnson closed 8 years ago

jason-johnson commented 9 years ago

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.

ekmett commented 9 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.

ekmett commented 9 years ago

On the other-other hand, these instances are inconsistent with the behavior of MaybeT and ListT.

ekmett commented 8 years ago

Deciding not to proceed here because of the incompatibility with the existing MaybeT and ListT instances.

Ericson2314 commented 8 years ago

Sorry what's the incompatibility/inconsistency with MaybeT?

ekmett commented 8 years ago

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.

Ericson2314 commented 8 years ago

Ah of course, thanks. Maybe it's just best to add a catchM to transformers then.