🎓 A1Learner
🔍
0 of 50 lessons completed

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?