20-CS-4003-001 Organization of Programming Languages Fall 2017
Exceptions

Lambda calculus, Type theory, Formal semantics, Program analysis

    Prev     Next     All lectures           Code

try, handle, and handleJust

import Control.Exception
v1 = evaluate(5 `div` 0)
v2 = evaluate(27 `div` 5)

{- Customize the result of an exception -}
v3 = try (v1)::IO (Either SomeException Integer)
v4 = try (v2)::IO (Either SomeException Integer)

v5 = print "Hello World"
v6 = try (v5)::IO (Either SomeException ())
v7 = print (2/0)
v8 = try (v7)::IO (Either SomeException ())
v9 = readFile "NoSuchFile"
v10 = try (v9)::IO (Either SomeException String)
v11 = readFile "exception.0.hs"
v12 = try (v11)::IO (Either SomeException String)

func x = "Got this exception:" ++ (show x)

exHandler :: SomeException -> IO ()
exHandler x = putStrLn $ func x

v13 = handle exHandler $ print (4 `div` 2)
v14 = handle exHandler $ print (4 `div` 0)
v15 = handle exHandler $ print (tail []::[Char])
v16 = handle exHandler $ print ([1..10] !! 12)

{- demonstration of handleJust -}
{- catch two ArithExceptions -}
catchIt DivideByZero = (Just "Whoa Nelly") 
catchIt LossOfPrecision = (Just "Zowie");
catchIt _ = Nothing 

saHandler x = putStrLn $ func x

{- safePrint :: Integer -> IO () -}
safePrint x = handleJust catchIt saHandler (print x)

v17 = safePrint $ 5 `div` 0
v18 = safePrint $ 5 `div` 2
v19 = safePrint $ tail ([]::[Int])
v20 = safePrint $ ([1..10] !! 12)
  -   The first couple of functions to the left show that exceptions can be customized. Sample uncustomized outputs are:
*Main> evaluate(5 `div` 0)
*** Exception: divide by zero

*Main> evaluate(27 `div` 5)
5
Sample customized outputs are
*Main> try (evaluate (5 `div` 0))::IO (Either SomeException Integer)
Left divide by zero

*Main> try (evaluate (27 `div` 5))::IO (Either SomeException Integer)
Right 5

*Main> try (print "Hello World")::IO (Either SomeException ())
"Hello World"
Right ()

*Main> try (print (2/0))::IO (Either SomeException ())
Infinity
Right ()

*Main> try (readFile "NoSuchFile")::IO (Either SomeException String)
Left NoSuchFile: openFile: does not exist (No such file or directory)

*Main> try (readFile "exception.0.hs")::IO (Either SomeException String)
Right "\n{-\nHaskell is expressive enough to let you build your own..."

Often, one action is needed if a piece of code completes without an exception, and a different action is needed otherwise. For situations like this, there is a function called handle. Function handle has type

handle :: (Exception -> IO a) -> IO a -> IO a
That is, it takes two parameters: the first is a function to call in the event there is an exception while performing the second. Sample output is as follows:
*Main> handle exHandler $ print (4 `div` 2)
2

*Main> handle exHandler $ print (4 `div` 0)
Got this exception:divide by zero

*Main> handle exHandler $ print (tail []::[Int])
Got this exception:Prelude.tail: empty list

*Main> handle exHandler $ print (tail [1,2,3]::[Int])
[2,3]

*Main> handle exHandler $ print ([1..10] !! 12)
Got this exception: Prelude.(!!): index too large
In case it is desired to handle some exceptions and not others handleJust can be used. The first argument is a function that determines whether the second argument should be invoked in case of an exception. Sample output is as follows:
*Main> safePrint $ 5 `div` 0
Got this exception:"Whoa Nelly"

*Main> safePrint $ 5 `div` 2
2

*Main> safePrint $ tail ([]::[Int])
*** Exception: Prelude.tail: empty lis

*Main> safePrint $ ([1..10] !! 12)
*** Exception: Prelude.(!!): index too large

*Main> safePrint $ ([1..10] !! 3)
4