I have the following code:
handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ return $ [1, 2, 3] !! x
printException :: Int -> SomeException -> IO Int
printException x (SomeException e) = do
print ("Exception", x, e)
throw e
When I type handledIO 8
in ghci, I expect to see ("Exception", 8, "*** Exception: Prelude.!!: index too large")
will be printed, but actually only the exception is printed. Why?
The reason is a bit subtle and has to do with Haskell's laziness.
Let's use this version of your example that has the same problem:
handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ return undefined
The problem is that handle
only catches exceptions thrown when the IO
action passed as argument is running. The action return undefined
completes successfully because the undefined
is never scrutinized from inside the action. But when other IO
actions try to actually inspect the returned value—like, for example, trying to print it to the console—they encounter a nasty surprise.
One solution to this "lazy exception-throwing result value in IO
" problem is to use the evaluate
function from Control.Exception
instead of return
. evaluate
takes care to reduce the argument to WHNF before the IO
action returns, so it trips the exception:
handledIO :: Int -> IO Int
handledIO x = handle (printException x) $ evaluate $ undefined
Now it works:
*Main> handledIO 10
("Exception",10,Prelude.undefined
Also, you are using throw
inside an IO
action. When inside IO
actions, throwIO
is preferable to throw
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With