Suggested addition tryError (originally tryExceptT)
ddssff opened this issue · 11 comments
It seems to me that this function might make a useful addition to Control.Monad.Except:
-- | ExceptT analog to the 'try' function.
tryExceptT :: Monad m => ExceptT e m a -> ExceptT e m (Either e a)
tryExceptT = lift . runExceptT
Can you give an example of where one might find this useful?
ExceptT e m (Either e a)
is the same as m (Either e (Either e a))
, nesting the Either
s.
Well, its a small thing, I just noticed it when trying to understand the relationship between GHC exceptions, Ed Kmett's exceptions library, and mtl's Control.Monad.Except. All but mtl have try-like functions that expose the Either in the return value. I'm not sure simple examples will be very convincing though.
λ> runExceptT $ tryExceptT (throwError "an error occurred") >>= either (\e -> liftIO (putStrLn (show e)) >> return 2) return
"an error occurred"
Right 2
Since ExceptT has a MonadCatch instance, this is just Control.Monad.Catch.try. However that requires users to depend on the exceptions library. So, I don't think I would be opposed or not opposed to adding this. Maybe someone else could chime in.
Since this is mtl it could be generalized to tryError :: MonadError m => m a -> m (Either e a)
, and the specialized tryExceptT
could be added to transformers
. I've found try
to be pretty useful, so I'm somewhat surprised mtl doesn't already have it.
Ah, that would be
tryError :: MonadError e m => m a -> m (Either e a)
tryError action = (Right <$> action) `catchError` (return . Left)
This seems like a good idea to me.
Also, a generalization of withExceptT:
withError :: (MonadError e m, MonadError e' m) => (e -> e') -> m a -> m a
withError f action = tryError action >>= either (throwError . f) return
Actually this withError implementation doesn't work, because there is a functional dependency m -> e. This more limited implementation works:
withError :: MonadEror e m => (e -> e) -> m a -> m a
withError f action = tryError action >>= either (throwError . f) return
However, this analogue of mapExceptT works:
mapError :: (MonadError e m, MonadError e' n) => (m (Either e a) -> n (Either e' b)) -> m a -> n b
mapError f action = f (tryError action) >>= liftEither
@ddssff would you like to prepare a PR? I'm currently in favour, and others are too.
Yes, I will do this.