[Yet more ch20 work John Goerzen **20080506054900] { addfile ./examples/ch20/input.txt addfile ./examples/ch20/toupper-impch20.hs hunk ./en/ch20-errors.xml 497 + + Of particular interest, you might notice the + ioErrors test, which corresponds to the + large class of I/O-related exceptions. + + + + + I/O Exceptions + + Perhaps the largest source of exceptions in any program is + I/O. All sorts of things can go wrong when dealing with the + outside world: disks can be full, networks can go down, or + files can be empty when you expect them to have data. In + Haskell, an I/O exception is just like any other exception in + that can be represented by the &Exception; data type. On the + other hand, because there are so many types of I/O exceptions, + a special module -- System.IO.Error exists + for dealing with them. + + + System.IO.Error defines two functions: + &catch; and &try; which, like their counterparts in + Control.Exception, are used to deal with + exceptions. Unlike the Control.Exception + functions, however, these functions will only trap I/O errors, + and will pass all other exceptions through uncaught. In + Haskell, I/O errors all have type IOError, + which is defined as the same as + IOException. + + + + Because both System.IO.Error and + Control.Exception define functions with + the same names, if you import both in your program, you will + get an error message about an ambiguous reference to a + function. You can import one or the other module + qualified, or hide the symbols from one + module or the other. Also, note that + Prelude defines the + System.IO.Error version of &catch;. + + + + Let's take a look at one approach to using exceptions in the + I/O system to our benefit. Back in , we presented a program that used an + imperative style to read lines from a file one by one. + Although we subsequently demonstrated more compact, "Haskelly" + ways to solve that problem, let's revisit that example here. + In the mainloop function, we had to + explicitly test if we were at the end of the input file before + each attempt to read a line from it. Instead, we could check + if the attempt to read a line resulted in an EOF error, like + so: + + &toupper-impch20.hs; + + Here, we use the System.IO.Error version of + &try; to check whether hGetLine raised an + &IOError;. If it did, we use isEOFError + (defined in System.IO.Error) to see if the + raised exception indicated that we reached the end of the + file. If it did, we exit the loop. If the exception was + something else, we call &ioError; to re-raise it. + + + There are many such tests and ways to extract information from + &IOError; defined in System.IO.Error. We + recommend that you consult that page in the library reference + when you need to know about them. + + + + + Raising Exceptions + + Thus far, we have talked in detail about handling exceptions. + There is another piece to the puzzle: raising exceptions. In + the examples we have visited so far in this chapter, the + Haskell system raises exceptions for you. However, it is + possible to raise any exception yourself. We'll show you how. + hunk ./examples/ch20/input.txt 1 +This is ch20/input.txt + +Test Input +I like Haskell +Haskell is great +I/O is fun + +123456789 hunk ./examples/ch20/toupper-impch20.hs 1 +{-- snippet all --} +-- ch20/toupper-impch20.hs + +import System.IO +import System.IO.Error +import Data.Char(toUpper) + +main :: IO () +main = do + inh <- openFile "input.txt" ReadMode + outh <- openFile "output.txt" WriteMode + mainloop inh outh + hClose inh + hClose outh + +mainloop :: Handle -> Handle -> IO () +mainloop inh outh = + do input <- try (hGetLine inh) + case input of + Left e -> + if isEOFError e + then return () + else ioError e + Right inpStr -> + do hPutStrLn outh (map toUpper inpStr) + mainloop inh outh +{-- /snippet all --} + }