Haskell IO Exceptions
A Haskell IO exception is a runtime failure from an IO action, like a missing file. It is caught with try or catch instead of being returned as a value.
Try catches a missing file
The file does not exist. try turns that failure into a Left value.
import Control.Exception (try, IOException, evaluate)
main :: IO ()
main = do
r <- try (do t <- readFile "no-such-file.txt"; _ <- evaluate (length t); return t) :: IO (Either IOException String)
putStrLn (case r of Left _ -> "caught"; Right _ -> "found")Try keeps a good read on the Right
The file exists with one word. try keeps its text on the Right side.
import Control.Exception (try, IOException, evaluate)
main :: IO ()
main = do
writeFile "io-good.txt" "hello"
r <- try (do t <- readFile "io-good.txt"; _ <- evaluate (length t); return t) :: IO (Either IOException String)
putStrLn (case r of Right txt -> txt; Left _ -> "caught")ArithException needs its own handler
Dividing by zero is an arithmetic failure. try with ArithException catches it.
import Control.Exception (try, ArithException, evaluate)
main :: IO ()
main = do
r <- try (evaluate (1 `div` 0)) :: IO (Either ArithException Int)
putStrLn (case r of Left _ -> "caught-arith"; Right _ -> "ok")Example
import Control.Exception (try, IOException, ArithException, evaluate)
main :: IO ()
main = do
writeFile "io-ex-good.txt" "hello"
good <- try (do t <- readFile "io-ex-good.txt"; _ <- evaluate (length t); return t) :: IO (Either IOException String)
missing <- try (do t <- readFile "no-such-ex.txt"; _ <- evaluate (length t); return t) :: IO (Either IOException String)
arith <- try (evaluate (1 `div` 0)) :: IO (Either ArithException Int)
putStrLn (case good of Right _ -> "found"; Left _ -> "caught")
putStrLn (case missing of Left _ -> "caught"; Right _ -> "found")
putStrLn (case arith of Left _ -> "caught-arith"; Right _ -> "ok")Key points
- IOException: an IOException is the failure type for file and device problems.
- Try: try turns an IO failure into an Either value with Left for failure and Right for success.
- Catch: catch runs a handler when an IO action fails with a chosen failure type.
- ArithException: an ArithException is the failure type for bad arithmetic like division by zero.
- Separate types: catching IOException does not catch an ArithException.
In this example, the program writes a good file and then tries three actions. It reads the good file, reads a missing file, and divides by zero. It prints one outcome line for each action.
Note
an uncaught IO exception stops the program, so a demo of one must catch it and print the result.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
Which handler catches division by zero?