Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Exception can't be handled

Tags:

haskell

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?

like image 346
Leo Zhang Avatar asked Dec 12 '18 07:12

Leo Zhang


Video Answer


1 Answers

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.

like image 101
danidiaz Avatar answered Nov 01 '22 08:11

danidiaz