Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.
What do you think of designing API functions to use MonadError to handle errors instead of MonadThrow? I think the former is better because you specify the exact type of the error the API will return.
The path library doesn't do this, so I've initiated a discussion there: https://github.com/commercialhaskell/path/issues/149
I tend to prefer MonadError too, it makes error code/handling more explicit
IMO, they serve different purposes—MonadThrow and MonadCatch are really about dealing with IO exceptions (though they have some other instances), and I mostly avoid MonadThrow because I usually don’t want to use IO exceptions. I’d much rather deal with an API that provides MonadError constraints than one that provides MonadThrow constraints.
That said, it’s worth noting that MonadMask is a different beast: it’s fundamentally something MonadError cannot provide, because MonadMask allows you to implement a finally operation that runs if any monad in the stack causes an abort. For example, if you have ExceptT e IO, Control.Monad.Catch.finally will execute the action whether you raise an IO exception or call throwError. In that sense, MonadMask is “deep” while MonadError is “shallow.” You need MonadMask if you need reliable recovery from aborts; MonadError just won’t cut it.
ExceptT e IO
Why does MonadError e m have the functional dependency m -> e?
MonadError e m
m -> e
@Sridhar Ratnakumar From instances it seems like underlying m always determines e
Well I'm looking at the Either instance - and I don't see Either e determining e ... oh wait!
Okay this does surprise me at first glance: pasted image
It is surprising, because here I'm thinking "why doesn't compiler use one instance for the first let statement, and another for the other let statement"
Looks like the type annotation should be in the _s, not on the left side.
That's not it.
Even this fails:
main :: IO ()
main = do
() <- maybe (fail "Invalid") pure $ someFunc @Maybe
_v <- either (fail . show) pure $ someFunc @(Either MyError)
@Sridhar Ratnakumar https://www.stackage.org/haddock/lts-14.15/mtl-2.2.2/Control-Monad-Error.html#t:MonadError - there's only one instance for Maybe (with ()) - but someFunc wants one that uses MyError
(There can only be one because of fundep)
Yea, the question is why is the second invocation not using the Either instance?
Why does the compiler not use two instances in the same function (main)?
I mean, I explicitly specified Either MyError as the monad in the second invocation; so GHC should have no trouble, in theory, figuring out the right instance to use?
Error talks about first invocation
Uh, right. I didn't see. Maybe was a bad choice.
I'm trying to understand the problem Chris points to in the 3rd paragraph of https://github.com/commercialhaskell/path/issues/149#issuecomment-558069993
So if I use a custom error type (MyError) with MonadError - the only instance I use for the m is Either e?
(plus, transformer versions)
You couldn't use it with IO, because IO is bound to IOException (even though it could possibly throw any), while Maybe wouldn't work because it's bound to () (even though you could just ignore error's content and return Nothing)
main :: IO ()
main = do
() <- either throwM pure someFunc
let _ :: Either MyError () = someFunc
IO and Either e at the same time.
Actually, now that I think about it - catchError cannot be implemented properly for polymorphic Maybe instance, because you lose original error
You could Monoid yourself out though if it's content is not important
You can have safe MonadThrow e Maybe though because catch is in MonadCatch
MonadThrow e Maybe
Think of it this way - MonadError sort of requires m to "keep" original error in case it occurs (catchError is "proof" of that) - this can't be satisfied by Maybe properly
But MonadThrow separates that proof to MonadCatch - so even though Maybe can't have MonadCatch instance, it can have MonadThrow
Yes, but with MonadThrow|Catch we are dealing with bottoms which "bypass" a strict type system.