[Ch20 progress John Goerzen **20080503154008] { hunk ./en/ch20-errors.xml 342 - working with exceptions, and we'll show you how in this - section. But before we begin, we need to give you some - background on how exceptions fit into the Haskell language. + working with exceptions. In Haskell, exceptions may be raised + from any location in the program. However, due to lazy + evaluation, they can only be caught in the &IO; monad. Haskell + exception handling doesn't involve special syntax as it does in + Python or Java. Rather, the mechanisms to catch and handle + exceptions are -- surprise -- functions. hunk ./en/ch20-errors.xml 349 - - Exceptions in Haskell + + First Steps with Exceptions hunk ./en/ch20-errors.xml 352 + In the Control.Exception module, various + functions and types relating to exceptions are defined. There + is an &Exception; type defined there; all exceptions are of + type &Exception;. There are also functions for catching and + handling exceptions. Let's start by looking at &try;, which + has type IO a -> IO (Either Exception + a). This wraps an &IO; action with exception + handling. If an exception was raised, it will return a &Left; + value with the exception; otherwise, a &Right; value with the + original result. Let's try this out in &ghci;. We'll first + trigger an un-handled exception, and then try to catch it. + + &exc.ghci:try1; + + Notice that no exception was + raised by the &let; statements. That's to be expected due to + lazy evaluation; the division by zero won't be attempted until + it is demanded by the attempt to print out + x. Also, notice that there were two lines + of output from try (print y). The first + line was produced by print, which displayed + the digit 5 on the terminal. The second was produced by + &ghci;, which is showing you that print y + returned () and didn't raise an exception. + + + + Laziness and Exception Handling + + Now that you know how &try; works, let's try another + experiment. Let's say we want to catch the result of + try for future evaluation, so we can handle + the result of division. Perhaps we + would do it liks this: + + &exc.ghci:try2; + + What happened here? Let's try to piece it together, and + illustrate with another attempt: + + &exc.ghci:try3; + + As before, assigning &undefined; to z was + not a problem. The key to this puzzle, and to the division + puzzle, lies with lazy evaluation. Specifically, it lies with + &return;, which does not force the evaluation of its argument; + it only wraps it up. So, the result of try (return + undefined) would be Right + undefined. Now, &ghci; wants to display this result + on the terminal. It gets as far as printint out + "Right ", but you can't print out + &undefined; (or the result of division by zero). So when you + see the exception message, it's coming from &ghci;, not your + program. + + + This is a key point. Let's think about why our earlier + example worked and this one didn't. Earlier, we put + print x inside &try;. Printing the value + of something, of course, requires it to be evaluated, so the + exception was detected at the right place. But simply using + &return; does not force evaluation. To solve this problem, + the Control.Exception module defines the + &evaluate; function. It behaves just like &return;, but + forces its argument to be evaluated immediately. Let's try + it: + + &exc.ghci:try4; + + There, that's what was expected. This worked for both + &undefined; and our division by zero example. + + + + Remember: whenever you are trying to catch exceptions raised + by pure code, use &evaluate; instead of &return; inside your + exception-catching function. + + + + + +Let's look at one: &handle;. The type + of &handle; is (Exception -> IO a) -> IO a + -> IO a. That is, &handle; takes two arguments: + a function that is called if there + + addfile ./examples/ch20/exc.ghci hunk ./examples/ch20/exc.ghci 1 +--# try1 +:m Control.Exception +let x = 5 `div` 0 +let y = 5 `div` 1 +print x +print y +try (print x) +try (print y) +--# try2 +result <- try (return x) +--# try3 +let z = undefined +try (print z) +result <- try (return z) +--# try4 +let z = undefined +result <- try (evaluate z) +result <- try (evaluate x) }