Trying to understand error-catching in Haskell - haskell

While trying to write a program in Haskell, I suddenly realized that I apparently don't understand how error throwing/catching excetions works. While my actual case is significantly more complicated, I've come up with a seemingly minimal example displaying what I don't understand:
import Control.Exception
import Control.Monad
import Data.Typeable
data IsFalse = IsFalse
deriving (Show, Typeable)
instance Exception IsFalse
isTrue :: Bool -> Bool
isTrue b = if b then b else throw IsFalse
catchesFalse :: Bool -> IO ()
catchesFalse = try . return . isTrue >=> either (\e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")
main :: IO ()
main = catchesFalse False
When running with runhaskell, I would expect the above code to fail and print IsFalse. However, it instead prints uh-oh. On the other hand, if I replace the definition of catchesFalse by
catchesFalse = try . return . isTrue >=> either (\e -> fail $ displayException (e :: IsFalse)) print
then the exception is caught, just as I would expect.
I'm hoping that someone can point me to any resources that could help me understand the discrepency between these two functions. My best guess is that there's something going on with lazy evaluation, but I'm not sure.
If this is indeed the case, what's the best method to force Haskell to evaluate an expression to the point where it could catch an exception? Forgive me, I understand that this particular question likely has many answers depending on what I actually care to evaluate (which, in my actual case, isn't anywhere near as simple as Bool).

What you probably want is evaluate:
catchesFalse = try . evaluate . isTrue >=> either (\e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")
With this definition, catchesFalse False will result in
*** Exception: user error (IsFalse)
Note that the user error here is a hint that this has actually been produced by fail.
Both your examples don't "catch" the exception. The second one triggers it by means of calling print.
Exceptions in "pure" (i.e., non-IO) computations are tricky. In fact, we have the following equalities
try (return e) >>= f
=
return (Right e) >>= f
=
f (Right e)
Let's look at the first equation, which is probably the more surprising. The function try is implemented in terms of catch, and catch wraps the given IO computation and checks whether in its execution there are any effects. However, execution does not mean evaluation, and it only concerns the "effectful" part of the computation. A return is a trivial IO computation that "succeeds" immediately. Neither catch nor try are going to act on this, regardless of what the result looks like.
The second equation simply follows from the monad laws.
If we keep this in mind, and apply equational reasoning to your examples, we get in the first case:
catchesFalse False
=
(try . return . isTrue >=> either (\ e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")) False
=
try (return (isTrue False)) >>= either (\ e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")
=
return (Right (isTrue False)) >>= either (\ e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")
=
either (\ e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh") (Right (isTrue False))
=
(const $ putStrLn "uh-oh") (isTrue False)
=
putStrLn "uh-oh"
So as you can see, the exception is never even triggered.
In the second example, everything is the same until almost the end, and we get
either (\ e -> fail $ displayException (e :: IsFalse)) print (Right (isTrue False))
=
print (isTrue False)
Now, when executing this, print will force its argument, and thereby trigger the exception, and this will yield the output:
*** Exception: IsFalse
This is coming directly from throw, not from your handler; there's not user error in the output.
The use of evaluate changes this in returning an IO action that forces its argument to weak head normal form before "returning", thereby lifting a certain amount of exceptions that arise during evaluation of the argument expression into exceptions that can be caught during execution of the resulting IO action.
Note, however, that evaluate does not fully evaluate its argument, but only to weak head normal form (i.e., the outermost constructor).
All in all, a lot of care is necessary here. In general, it is advisable to avoid exceptions in "pure" code, and to use types that explicitly allow failure (such as Maybe and variants) instead.

Related

Enforcing strictness in Haskell

While doing some TTD in Haskell, I recently developed the following function:
import Test.HUnit
import Data.Typeable
import Control.Exception
assertException :: (Show a) => TypeRep -> IO a -> Assertion
assertException errType fun = catch (fun >> assertFailure msg) handle
where
msg = show errType ++ " exception was not raised!"
handle (SomeException e) [...]
The function takes a Type representation of an expected exception and an IO action. The problem is that most of the time I don't get the exception thrown even though I should have been, because of laziness. Often failing parts of fun are actually never evaluated here.
To remedy this i tried to replace (fun >> assertFailure msg) with (seq fun $ assertFailure msg). I also tried to enable BangPatterns extension and put a bang before fun binding, but none of it helped. So how can I really force Haskell to evaluate fun strictly?
You have to distinguish between:
Evaluating the value of type IO a
Running the action represented by it, which may have side effects and returns a value of type a, and
Evaluating the result of type a (or parts of it).
These always happen in that order, but not necessarily all of it. The code
foo1 :: IO a -> IO ()
foo1 f = do
seq f (putStrLn "done")
will do only the first, while
foo2 :: IO a -> IO ()
foo2 f = do
f -- equivalent to _ <- f
putStrLn "done"
also does the second and finally
foo3 :: IO a -> IO ()
foo3 f = do
x <- f
seq x $ putStrLn "done"
also does the third (but the usual caveats of using seq on a complex data type like lists apply).
Try these arguments and observe that foo1, foo2 and foo3 treat them differently.
f1 = error "I am not a value"
f2 = fix id -- neither am I
f3 = do {putStrLn "Something is printed"; return 42}
f4 = do {putStrLn "Something is printed"; return (error "x has been evaluated")}
f5 = do {putStrLn "Something is printed"; return (Just (error "x has been deeply evaluated"))}
You probably need to force the value to its normal form, not just its weak head normal form. For example, evaluating Just (error "foo") to WHNF won't trigger the exception, it'll just evaluate Just. I'd use the combination of evaluate (which allows to properly sequence forced evaluation with IO actions) and rnf (or force if you'd need the value for something):
assertException :: (Show a) => TypeRep -> IO a -> Assertion
assertException errType fun =
catch (fun >>= evaluate . rnf >> assertFailure msg) handle
where ...
However, be careful, as assertFailure is implemented using exceptions, so wrapping into the catch block might catch it as well. So I'd suggest to evaluate the computation using try and call assertFailure outside the try block:
import Test.HUnit
import Data.Typeable
import Control.DeepSeq
import Control.Exception
assertException :: (NFData a, Show a) => TypeRep -> IO a -> Assertion
assertException errType fun =
(try (fun >>= evaluate . rnf) :: IO (Either SomeException ())) >>= check
where
check (Right _) =
assertFailure $ show errType ++ " exception was not raised!"
check (Left (SomeException ex))
| typeOf ex == errType = return () -- the expected exception
| otherwise = assertFailure
$ show ex ++ " is not " ++ show errType

How do I abstract this pattern in haskell?

Scenario: I have an interpreter that builds up values bottom-up from an AST. Certain nodes come with permissions -- additional boolean expressions. Permission failures should propagate, but if a node above in the AST comes with a permission, a success can recover the computation and stop the propagation of the error.
At first I thought the Error MyError MyValue monad would be enough: one of the members of MyError could be PermError, and I could use catchError to recover from PermError if the second check succeeds. However, MyValue is gone by the time I get to the handler. I guess there could ultimately be a way of having PermError carry a MyValue field so that the handler could restore it, but it would probably be ugly and checking for an exception at each step would defeat the concept of an exceptional occurrence.
I'm trying to think of an alternative abstraction. Basically I have to return a datatype Either AllErrorsExceptPermError (Maybe PermError, MyValue) or more simply (Maybe AllErrors, MyValue) (the other errors are unrecoverable and fit the error monad pretty well) and I'm looking for something that would save me from juggling the tuple around, since there seems to be a common pattern in how the operations are chained. My haskell knowledge only goes so far. How would you use haskell to your advantage in this situation?
While I write this I came up with an idea (SO is a fancy rubber duck): a Monad that that handles internally a type (a, b) (and ultimately returns it when the monadic computation terminates, there has to be some kind of runMyMonad), but lets me work with the type b directly as much as possible. Something like
data T = Pass | Fail | Nothing
instance Monad (T , b) where
return v = (Nothing, v)
(Pass, v) >>= g = let (r', v') = g v in (if r' == Fail then Fail else Pass, v')
(Fail, v) >>= g = let (r', v') = g v in (if r' == Pass then Pass else Fail, v')
(Nothing, _) >>= g = error "This should not have been propagated, all chains should start with Pass or Fail"
errors have been simplified into T, and the instance line probably has a syntax error, but you should get the idea. Does this make sense?
I think you can use State monad for permissions and value calculation and wrap that inside ErrorT monad transformer to handle the errors. Below is such an example which shows the idea , here the calculation is summing up a list, permissions are number of even numbers in the list and error condition is when we see 0 in the list.
import Control.Monad.Error
import Control.Monad.State
data ZeroError = ZeroError String
deriving (Show)
instance Error ZeroError where
fun :: [Int] -> ErrorT ZeroError (State Int) Int
fun [] = return 0
fun (0:xs) = throwError $ ZeroError "Zero found"
fun (x:xs) = do
i <- get
put $ (if even(x) then i+1 else i)
z <- fun xs
return $ x+z
main = f $ runState (runErrorT $ fun [1,2,4,5,10]) 0
where
f (Left e,evens) = putStr $ show e
f (Right r,evens) = putStr $ show (r,evens)

What's the meaning of IO actions within pure functions?

I thought that in principle Haskell's type system would forbid calls to impure functions (i.e. f :: a -> IO b) from pure ones, but today I realized that by calling them with return they compile just fine. In this example:
h :: Maybe ()
h = do
return $ putStrLn "???"
return ()
h works in the Maybe monad, but it's a pure function nevertheless. Compiling and running it simply returns Just () as one would expect, without actually doing any I/O. I think Haskell's laziness puts the things together (i.e. putStrLn's return value is not used - and can't since its value constructors are hidden and I can't pattern match against it), but why is this code legal? Are there any other reasons that makes this allowed?
As a bonus, related question: in general, is it possible to forbid at all the execution of actions of a monad from within other ones, and how?
IO actions are first-class values like any other; that's what makes Haskell's IO so expressive, allowing you to build higher-order control structures (like mapM_) from scratch. Laziness isn't relevant here,1 it's just that you're not actually executing the action. You're just constructing the value Just (putStrLn "???"), then throwing it away.
putStrLn "???" existing doesn't cause a line to be printed to the screen. By itself, putStrLn "???" is just a description of some IO that could be done to cause a line to be printed to the screen. The only execution that happens is executing main, which you constructed from other IO actions, or whatever actions you type into GHCi. For more information, see the introduction to IO.
Indeed, it's perfectly conceivable that you might want to juggle about IO actions inside Maybe; imagine a function String -> Maybe (IO ()), which checks the string for validity, and if it's valid, returns an IO action to print some information derived from the string. This is possible precisely because of Haskell's first-class IO actions.
But a monad has no ability to execute the actions of another monad unless you give it that ability.
1 Indeed, h = putStrLn "???" `seq` return () doesn't cause any IO to be performed either, even though it forces the evaluation of putStrLn "???".
Let's desugar!
h = do return (putStrLn "???"); return ()
-- rewrite (do foo; bar) as (foo >> do bar)
h = return (putStrLn "???") >> do return ()
-- redundant do
h = return (putStrLn "???") >> return ()
-- return for Maybe = Just
h = Just (putStrLn "???") >> Just ()
-- replace (foo >> bar) with its definition, (foo >>= (\_ -> bar))
h = Just (putStrLn "???") >>= (\_ -> Just ())
Now, what happens when you evaluate h?* Well, for Maybe,
(Just x) >>= f = f x
Nothing >>= f = Nothing
So we pattern match the first case
f x
-- x = (putStrLn "???"), f = (\_ -> Just ())
(\_ -> Just ()) (putStrLn "???")
-- apply the argument and ignore it
Just ()
Notice how we never had to perform putStrLn "???" in order to evaluate this expression.
*n.b. It is somewhat unclear at which point "desugaring" stops and "evaluation" begins. It depends on your compiler's inlining decisions. Pure computations could be evaluated entirely at compile time.

Extracting a Maybe value in IO

Given the following:
> (liftM2 fromMaybe) (ioError $ userError "OOPS") (return $ Just "ok")
ghci gives me
*** Exception: user error (OOPS)
Of course, fromMaybe is working correctly:
> (liftM2 fromMaybe) (return $ "not me") (return $ Just "ok")
"ok"
But it seems that the IO operation is being carried out and then discarded:
> (liftM2 fromMaybe) (putStrLn "computing.." >> "discarded") (return $ Just "ok")
computing..
"ok"
Why is this happening? Is there any way to make the IO monad lazier?
Specifically, given value :: IO (Maybe a) what's a (clean, concise) way to say
result <- (liftM2 fromMaybe) err value
and have it unpack result or throw an IOError accordingly?
I don't know that making IO lazier is the right direction here. What you seem to want to do is first get at the Maybe, then eliminate it. This can be written several ways, here's one option:
test :: IO (Maybe a) -> IO a
test = (>>= maybe (ioError $ userError "oops") return)
If you translate from liftM2 to do-notation, it's obvious why your code fails:
do x <- ioError $ userError "OOPS"
y <- return $ Just "ok"
return $ fromMaybe x y
This will never go past the first line, as it's unconditionally throwing an exception.
Anthony's suggestion will work fine, but if you don't care about the specific exception thrown, you can also use pattern matching:
do Just result <- value
If the pattern doesn't match, this will call fail, which in the case of the IO monad throws an exception.
> Just x <- return Nothing
*** Exception: user error (Pattern match failure in do expression at <interactive>:1:0-5)
what's a (clean, concise) way to ... unpack [the] result or throw an IOError accordingly?
I recommend you avoid relying on throwing errors. Instead, handle the "error" explicitly:
maybeM :: Monad m => m b -> (a -> m b) -> m (Maybe a) -> m b
maybeM err f value = do
x <- value
case x of
Just y -> f y
Nothing -> err
-- This can be written simply as:
maybeM err f value = do
x <- value
maybe err f x
-- or even shorter! This is starting to look like Anthony's answer :)
maybeM err f value = value >>= maybe err f
The function's inputs and types should speak for themselves. You use it by giving it an action to perform for the Nothing case, or a function to perform on the value inside for the Just case. For your particular inputs this would look like:
maybeM (ioError $ userError "OOPS") return (return $ Just "ok")
So if you absolutely must, then the "concise way to unpack the result or throw an IOError" would be:
-- compare to fromJust, a function to be avoided
fromJustIO :: IO (Maybe a) -> IO a
fromJustIO = maybeM (ioError $ userError "OOPS") return
Notice how the type signature for this is practically Maybe a -> a, which is the essence of magicMonadUnwrap :: Monad m => m a -> a, which should set off some red flags. However, you can use this atrocity in a simple manner:
result <- fromJustIO value
Although again, I strongly discourage the use of exceptions here. Try handling errors in a more elegant way than simply exploding, by using maybeM and providing an IO action to execute in the event of failure.

ErrorT catchError in practice

I have a very typical setup with a set of functions in the IO monad that can throw errors. To date I have just been dealing with errors at the end of the monad chain by pattern matching the Either result from runErrorT:
replLisp :: LispScope -> String -> IO String
replLisp s input = do
result <- runErrorT (evalLisp s input)
return $ either (id) (show) result
I would now like to add some error handling to my Hacked little scheme, but I'm having trouble making the type checker happy.
How does one use catchError? An example or two would be helpful.
This is my latest attempt:
catch :: [LispVal] -> IOThrowsError LispVal
catch [action rescue] = do
eval action >>= catchError $ eval rescue
Here is an example use of catchError to recover from a prior call to throwError:
import Control.Monad.Error
import Control.Monad.Identity
type MyMonad = ErrorT String Identity
runMyMonad = runIdentity . runErrorT
main = do
let x = runMyMonad (func 5 0)
print x
func :: Double -> Double -> MyMonad Double
func w x = do
y <- (divider x) `catchError` (\_ -> return 1)
return (w + y)
divider :: Double -> MyMonad Double
divider x = do
when (x == 0) (throwError "Can not divide by zero!")
return (10 / x)
Despite passing 0 in for division we can complete with the handlers result of 1 to obtain output of Right 6.0.
Does this help? Your question didn't really say what the issue was.
Error monads like Either and Maybe don't allow you to observe the error from within the same monad: you have to run the monad in order to observe it. Exceptions in IO are one notable exception (ahem) because IO is the end of the line... you can't go any further from there.
You have a few possibilities:
Since you're writing a mini-interpreter, it's probably a good idea to explicitly manage all the exceptions, using the ErrorT monad only for true, unrecoverable errors.
For any call that may error that you want to be able to recover from, perform a runErrorT and inspect that result, before passing along the result in the current monad.

Resources