#djinn MonadError e m => m () - haskell

Is it possible to give an expression of type
MonadError e m => m ()
that raises an error, that can be handled with catchError? Note the absence of an Error e requirement.
Under what circumstances can an error created with fail be handled with catchError? What can lead to a fail not being caught by catchError? In what way did these circumstances change with respect to GHC releases? (There is a mention that the behaviour changed in base 4.3.)
The background of this question is a larger piece of code (Igor2 function nomatch) where an error created with fail "some message" passes by catchError and terminates the program, but replacing the fail invocation with throwError undefined results in the error being caught as expected. Of course throwError undefined is an ugly hack and this question aims to understand the background and correct solution.

There are several separate issues here, so I'll go through them in order.
MonadError e m => m ()
The problem with the above is that if e is not constrained in any way, it is impossible to create a value of type e which you need for throwError. So no, the only valid value for the above type that raises an error would need to include
throwError undefined
which is a cop-out (and not very useful). The Error e constraint lets you leave e abstract by providing a mechanism to construct a value of type e from a string.
Under what circumstances can an error created with fail be handled with catchError? What can lead to a fail not being caught by catchError?
This depends entirely of the monad in question. Different instances of MonadError might have a different implementation for fail so there is no generic answer. Or to put it another way, there are no guarantees that the error triggered by fail can be handled by catchError unless you are using a specific instance of MonadError that makes that promise. For example:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Error
example :: (Error e, MonadError e m) => m String
example = fail "This is a failure" `catchError` const (return "Error was caught")
main = do
example >>= putStrLn
either putStrLn putStrLn example
In the first line of main, example uses the MonadError instance of IO and the second line uses the MonadError instance of Either String. fail results in a catchable error in IO but not in Either so the program outputs
Error was caught
*** Exception: This is a failure
However, if we replace example with
example :: (Error e, MonadError e m) => m String
example = throwError (strMsg "This is a failure")
`catchError` const (return "Error was caught")
Then we get the desired output
Error was caught
Error was caught
And this would work the same for any (valid) instance of MonadError, unlike fail.
In what way did these circumstances change with respect to GHC releases? (There is a mention that the behaviour changed in base 4.3.)
The change in base 4.3 refers to how fail works in the Either monad. In previous versions fail returned a Left value which could be caught with catchError but in 4.3 and after it uses error and triggers an exception that has to be handled in IO (with e.g. catch).
The problem in Igor2 sounds like it uses the Either monad and assumes the old behavior of fail when it should be using throwError (or simply Left if there is no suitable Error instance).

Related

How to use Handlers that don't have SomeException as their argument type

When using Control.Monad.Throw (i.e. exceptions package), something that confuses me that it seem that all my my throwing and catching has to work with SomeException.
E.g.
value :: Either ExitCode String
value = throwM $ ExitFailure 23
This looks to me like it should compile, because throwM :: (Exception e, MonadThrow m) => e -> m a, and ExitCode has an instance of Exception. Even this doesn't compile:
value :: Exception e => Either e String
value = throwM $ ExitFailure 23
In fact, it only compiles when I change the signature to SomeException. I know the Exception type class has a special place re
In the documentation for Control.Exception I could see examples where they use catches with handlers of signature ArithException -> m a or similiar. I tested it and it worked.
Is this not possible when using exceptions?
EDIT
The error messages are either:
Couldn't match type ExitCode with SomeException arising from a use of throwM
or
Couldn't match type e with SomeException arising from a use of throwM
The behavior you are seeing is from the type signature of throwM:
throwM :: (Exception e, MonadThrow m) => e -> m a
And the instance for Either, which is essentially:
MonadThrow (Either SomeException)
This makes throwM:
throwM :: (Exception e) => e -> Either SomeException a
throwM for Either SomeException can take any Exception e.
However, there is no MonadThrow instance for Either ExitCode, or for forall e. Exception e => Either e.
The problem is that there isn't really a way to write an instance that's polymorphic for all e. Imagine having an instance
Exception q => MonadThrow (Either q)
This would make throwM:
throwM :: (Exception e, Exception q) => e -> Either q a
which means you have to be able to take any e and convert it into any q, which is not possible using the Exception typeclass alone.
Imagine also if there was a MonadThrow instance for Either ExitCode. That would make the type signature for throwM:
throwM :: Exeption e => e -> Either ExitCode a
which you can probably see is clearly nonsensical, since there are many instances of Exception that cannot be coerced to an ExitCode. (Try writing a function with that type signature if you don't believe me!)
If you just want short-circuting exception behavior for Either, consider:
Just use Left instead of throwM, and pattern matching for catch. If you really want to use Exception still, you can use fromException to attempt to coerce a SomeException into an Exception instance of your choice.
You can use the exceptions package, and catching will still work as long as you must assume that the exception could be any instance (SomeException)
Use MonadError and ExceptT from mtl, if you want some sort of polymorphic interface with catching ability

Control.Exception.Safe, why do ExceptT and Either behave so differently?

I'm trying to use Control.Exception.Safe with Control.Monad.Except.
throwString "Foo" :: Except String a
-- error, no instance for `MonadTrow Identity`
Ok, so apparently Except throws its error into its underlying monad in the transformer stack?
But why is that? Isn't Except basically designed to handle exceptions? Why this weird behavior? Why not the equivalent of Left "Foo"
EDIT:
Okay to further illustrate my problem:
I thought ExceptT e m a was to Either e a what ReaderT a m b is to a -> b. Control.Monad.Except.throwError, and catchError work exactly like Control.Exception.Safe.throw, and catch do with Either e a.
However they suddenly work different when applied to Except e a.
What do I do, when I want to use the behaviour of Either e a that is supplied by Control.Exception.Safe but in the context of monad transformers?
My context is that I do "Write yourself a Scheme in 48 hours" and wanted to generalize errors (with MonadThrow), so that I can do some IO stuff with it.
EDIT2:
Example:
data CountError = CountError deriving (Show, Except)
x :: String -> ExceptT CountError (Writer [String]) Int
x str = do { lift $ writer (length "str", return str); }
Now this count characters and collects the strings in the writer. This could be extended however much you want. The error could signal "wrong character in string", or "too many characters", or whatever.
y :: MonadThrow m => m a
y = throw CountError
This is a very general exception, which I could use for composition with any other kind of exception, except for ExceptT:
y >> x
-- No instance for MonadThrow Identity
-- But what I want is (Left CountError, [])
Ok, so apparently Except throws its error into its underlying monad in the transformer stack?
As I said in the comment: Except doesn't "throw it's error into the underlying monad", that's just what the MonadThrow instances is doing.
But why is that?
The instance of MonadThrow can not throw a String exception into ExceptT e for all types e and rather than have an instance just for ExceptT String it appears the author lifted to throw an exception on the next higher monad.
Isn't Except basically designed to handle exceptions?
Indeed Except is designed to allow for failure in exceptional cases.
Being pedantic, I'd call it an alternative to exceptions. A monad plumbing an alternative notion of return (an error case) and calling itself "Except" doesn't actually mean any of the typical exception options, such as a low level stack unwinding, is in use.
Why this weird behavior?
Because of the MonadThrow instance, which is re-exported from Control.Monad.Throw:
-- | Throws exceptions into the base monad.
instance MonadThrow m => MonadThrow (ExceptT e m) where
throwM = lift . throwM
Why not the equivalent of Left "Foo"
Because then the instance would have to be for ExceptT String instead of ExceptT e. Or, that is why I think the author of exceptions (Edward Kmett) decided on this design.
Instead, consider using Control.Monad.Except.throwError which does what it sounds like you want.
What do I do, when I want to use the behaviour of Either e a that is supplied by Control.Exception.Safe but in the context of monad transformers?
What "behavior of Either e a are you talking about? How is what you are looking for different from throwError? As far as I can tell, you are looking for an unnecessary extra layer of abstraction.
This is a bit of an old question but I think there is room to improve on this answer as I recently ran into this topic at work. Let's see, I believe that your expectation of having ExceptT behave similar to Either makes sense, but unfortunately the author of MonadThrow chose a different stance and decided that "passing" the decision on how to "throw" a failure to the underlying monad (or transformer) was the right way to go. Without attempting to speculate why decision was made, I agree that this behavior defeats the purpose of using a "failable" transformer such as 'MaybeT' or 'ExceptT' as an instance of an error class like MonadThrow, which is to give "Maybe" or "Either" like semantics to a different monad.
For this reason I have donned my flame vest and took a stab at Control.Monad.Failable , which would do exactly what you (and I) would expect, which is to return something like Left e where e is an instance of Exception.
Control.Monad.Failable

How to preserve information when failing?

I'm writing some code that uses the StateT monad transformer to keep track of some stateful information (logging and more).
The monad I'm passing to StateT is very simple:
data CheckerError a = Bad {errorMessage :: Log} | Good a
deriving (Eq, Show)
instance Monad CheckerError where
return x = Good x
fail msg = Bad msg
(Bad msg) >>= f = Bad msg
(Good x) >>= f = f x
type CheckerMonad a = StateT CheckerState CheckerError a
It's just a Left and Right variant.
What troubles me is the definition of fail. In my computation I produce a lot of information inside this monad and I'd like to keep this information even when failing.
Currently the only thing I can do is to convert everything to a String and create a Bad instance with the String passed as argument to fail.
What I'd like to do is something like:
fail msg = do
info <- getInfoOutOfTheComputation
return $ Bad info
However everything I tried until now gives type errors, probably because this would mix different monads.
Is there anyway in which I can implement fail in order to preserve the information I need without having to convert all of it into a String?
I cannot believe that the best Haskell can achieve is using show+read to pass all the information as the string to fail.
Your CheckerError monad is very similar to the Either monad. I will use the Either monad (and its monad transformer counterpart ErrorT) in my answer.
There is a subtlety with monad trasformers: order matters. Effects in the "inner" monad have primacy over effects caused by the "outer" layers. Consider these two alternative definitions of CheckerMonad:
import Control.Monad.State
import Control.Monad.Error
type CheckerState = Int -- dummy definitions for convenience
type CheckerError = String
type CheckerMonad a = StateT CheckerState (Either String) a
type CheckerMonad' a = ErrorT String (State CheckerState) a
In CheckerMonad, Either is the inner monad, and this means a failure will wipe the whole state. Notice the type of this run function:
runCM :: CheckerMonad a -> CheckerState -> Either CheckerError (a,CheckerState)
runCM m s = runStateT m s
You either fail, or return a result along with the state up to that point.
In CheckerMonad', State is the inner monad. This means the state will be preserved even in case of failures:
runCM' :: CheckerMonad' a -> CheckerState -> (Either CheckerError a,CheckerState)
runCM' m s = runState (runErrorT m) s
A pair is returned, which contains the state up to that point, and either a failure or a result.
It takes a bit of practice to develop an intuition of how to properly order monad transformers. The chart in the Type juggling section of this Wikibook page is a good starting point.
Also, it is better to avoid using fail directly, because it is considered a bit of a wart in the language. Instead, use the specialized functions for throwing errors provided by the error transformer. When working with ErrorT or some other instance of MonadError, use throwError.
sillycomp :: CheckerMonad' Bool
sillycomp = do
modify (+1)
s <- get
if s == 3
then throwError "boo"
else return True
*Main> runCM' sillycomp 2
Loading package transformers-0.3.0.0 ... linking ... done.
Loading package mtl-2.1.2 ... linking ... done.
(Left "boo",3)
*Main> runCM' sillycomp 3
(Right True,4)
ErrorT is sometimes annoying to use because, unlike Either, it requires an Error constraint on the error type. The Error typeclass forces you to define two error constructors noMsg and strMsg, which may or may not make sense for your type.
You can use EitherT from the either package instead, which lets you use any type whatsoever as the error. When working with EitherT, use the left function to throw errors.

Non type-variable argument in the constraint: MonadError Failure m

I have defined a custom error type:
data Failure = NetworkError Message |
UserIsTooStupid Message |
InvalidOperation Message |
UnexpectedError Message
type Message = String
I am trying to use MonadError with my error type:
loadJSON :: (Aeson.FromJSON v, MonadIO m, MonadError Failure m) => URI -> m v
loadJSON uri = do
body <- liftIO $ getResponseBody =<< simpleHTTP (getRequest uri)
case Aeson.decode body of
Just json -> return json
Nothing -> throwError $ SerialisationError "Couldn't deserialise from JSON"
type URI = String
In other words, this function can return any monad which satisfies both MonadIO and MonadError, but the only type of error it can throw is Failure.
This fails to compile, with the error message:
Non type-variable argument in the constraint: MonadError Failure m
(Use -XFlexibleContexts to permit this)
In the type signature for `loadJSON':
loadJSON :: (Aeson.FromJSON v, MonadIO m, MonadError Failure m) =>
URI -> m v
GHC wants me to turn on the FlexibleContexts language extension to make this code work. What does FlexibleContexts do, and is it really necessary in my case? I prefer not to turn on language extensions willy-nilly without knowing whether I need them.
The function compiles fine if I leave off the type signature, but I prefer not to do that.
Haskell 98 doesn't allow constraints that looks like MonadError Failure m, they have to look like MonadError a m. The ability to have constraints like that was added to GHC however, which is what that extension does. I understand being wary of language extensions, but FlexibleContexts is pretty harmless.
I believe at the time Haskell 98 came out, the type theory to use constraints like that hadn't been developed, but since then it has. The extension turns on some extra code in the type checker which uses that theory. If you google around a little you might be able to find a paper on how it works.

Haskell: Why do the Maybe and Either types behave differently when used as Monads?

I'm trying to get my head around error handling in Haskell. I've found the article "8 ways to report errors in Haskell" but I'm confused as to why Maybe and Either behave differently.
For example:
import Control.Monad.Error
myDiv :: (Monad m) => Float -> Float -> m Float
myDiv x 0 = fail "My divison by zero"
myDiv x y = return (x / y)
testMyDiv1 :: Float -> Float -> String
testMyDiv1 x y =
case myDiv x y of
Left e -> e
Right r -> show r
testMyDiv2 :: Float -> Float -> String
testMyDiv2 x y =
case myDiv x y of
Nothing -> "An error"
Just r -> show r
Calling testMyDiv2 1 0 gives a result of "An error", but calling testMyDiv1 1 0 gives:
"*** Exception: My divison by zero
(Note the lack of closing quote, indicating this isn't a string but an exception).
What gives?
The short answer is that the Monad class in Haskell adds the fail operation to the original mathematical idea of monads, which makes it somewhat controversial how to make the Either type into a (Haskell) Monad, because there are many ways to do it.
There are several implementations floating around that do different things. The 3 basic approaches that I'm aware of are:
fail = Left. This seems to be what most people expect, but it actually can't be done in strict Haskell 98. The instance would have to be declared as instance Monad (Either String), which is not legal under H98 because it mentions a particular type for one of Eithers parameters (in GHC, the FlexibleInstances extension would cause the compiler to accept it).
Ignore fail, using the default implementation which just calls error. This is what's happening in your example. This version has the advantage of being H98 compliant, but the disadvantage of being rather surprising to the user (with the surprise coming at runtime).
The fail implementation calls some other class to convert a String into whatever type. This is done in MTL's Control.Monad.Error module, which declares instance Error e => Monad (Either e). In this implementation, fail msg = Left (strMsg msg). This one is again legal H98, and again occasionally surprising to users because it introduces another type class. In contrast to the last example though, the surprise comes at compile time.
I'm guessing you're using monads-fd.
$ ghci t.hs -hide-package mtl
*Main Data.List> testMyDiv1 1 0
"*** Exception: My divison by zero
*Main Data.List> :i Either
...
instance Monad (Either e) -- Defined in Control.Monad.Trans.Error
...
Looking in the transformers package, which is where monads-fd gets the instance, we see:
instance Monad (Either e) where
return = Right
Left l >>= _ = Left l
Right r >>= k = k r
So, no definition for Fail what-so-ever. In general, fail is discouraged as it isn't always guaranteed to fail cleanly in a monad (many people would like to see fail removed from the Monad class).
EDIT: I should add that it certainly isn't clear fail was intentioned to be left as the default error call. A ping to haskell-cafe or the maintainer might be worth while.
EDIT2: The mtl instance has been moved to base, this move includes removing the definition of fail = Left and discussion as to why that decision was made. Presumably, they want people to use ErrorT more when monads fail, thus reserving fail for something more catastrophic situations like bad pattern matches (ex: Just x <- e where e -->* m Nothing).

Resources