Error handling is one of the most important—and overlooked—topics for programmers, regardless of the language used. In Haskell, you will find two major types of error handling employed: pure error handling and exceptions.
When we speak of pure error handling, we are referring to algorithms that do not require anything from the IO monad. We can often implement error handling for them simply by using Haskell’s expressive data type system to our advantage. Haskell also has an exception system. Due to the complexities of lazy evaluation, exceptions in Haskell can be thrown anywhere, but caught only within the IO monad. In this chapter, we’ll consider both.
Let’s begin our discussion of error handling with a very simple function. Let’s say that we wish to perform division on a series of numbers. We have a constant numerator but wish to vary the denominator. We might come up with a function like this:
-- file: ch19/divby1.hs divBy :: Integral a => a -> [a] -> [a] divBy numerator = map (numerator `div`)
Very simple, right? We can play around with this a bit in ghci:
divBy 50 [1,2,5,8,10][50,25,10,6,5]
take 5 (divBy 100 [1..])[100,50,33,25,20]
This behaves as expected:
50 / 1 is
50 / 2
25, and so forth. This even worked with the infinite list
[1..]. What happens if we sneak a
0 into our list somewhere?
divBy 50 [1,2,0,8,10][50,25,*** Exception: divide by zero
Isn’t that interesting? ghci started displaying ...