# Exception

### From HaskellWiki

(link to Hackage) |
(ExAction -> Exceptional) |
||

Line 23: | Line 23: | ||

but we can handle situations, where it is unacceptable for the caller to check a priori whether the call can succeed. |
but we can handle situations, where it is unacceptable for the caller to check a priori whether the call can succeed. |
||

<haskell> |
<haskell> |
||

− | data ExAction e a = |
+ | data Exceptional e a = |

Success a |
Success a |
||

| Exception e |
| Exception e |
||

deriving (Show) |
deriving (Show) |
||

− | instance Monad (ExAction e) where |
+ | instance Monad (Exceptional e) where |

return = Success |
return = Success |
||

Exception l >>= _ = Exception l |
Exception l >>= _ = Exception l |
||

Success r >>= k = k r |
Success r >>= k = k r |
||

− | throw :: e -> ExAction e a |
+ | throw :: e -> Exceptional e a |

throw = Exception |
throw = Exception |
||

− | catch :: ExAction e a -> (e -> ExAction e a) -> ExAction e a |
+ | catch :: Exceptional e a -> (e -> Exceptional e a) -> Exceptional e a |

catch (Exception l) h = h l |
catch (Exception l) h = h l |
||

catch (Success r) _ = Success r |
catch (Success r) _ = Success r |
||

Line 44: | Line 44: | ||

This is not restricted to IO, but may be used immediately also for non-deterministic algorithms implemented with the <hask>List</hask> monad. |
This is not restricted to IO, but may be used immediately also for non-deterministic algorithms implemented with the <hask>List</hask> monad. |
||

<haskell> |
<haskell> |
||

− | newtype ExActionT e m a = |
+ | newtype ExceptionalT e m a = |

− | ExActionT {runExActionT :: m (ExAction e a)} |
+ | ExceptionalT {runExceptionalT :: m (Exceptional e a)} |

− | instance Monad m => Monad (ExActionT e m) where |
+ | instance Monad m => Monad (ExceptionalT e m) where |

− | return = ExActionT . return . Success |
+ | return = ExceptionalT . return . Success |

− | m >>= k = ExActionT $ |
+ | m >>= k = ExceptionalT $ |

− | runExActionT m >>= \ a -> |
+ | runExceptionalT m >>= \ a -> |

case a of |
case a of |
||

Exception e -> return (Exception e) |
Exception e -> return (Exception e) |
||

− | Success r -> runExActionT (k r) |
+ | Success r -> runExceptionalT (k r) |

− | throwT :: Monad m => e -> ExActionT e m a |
+ | throwT :: Monad m => e -> ExceptionalT e m a |

− | throwT = ExActionT . return . Exception |
+ | throwT = ExceptionalT . return . Exception |

catchT :: Monad m => |
catchT :: Monad m => |
||

− | ExActionT e m a -> (e -> ExActionT e m a) -> ExActionT e m a |
+ | ExceptionalT e m a -> (e -> ExceptionalT e m a) -> ExceptionalT e m a |

− | catchT m h = ExActionT $ |
+ | catchT m h = ExceptionalT $ |

− | runExActionT m >>= \ a -> |
+ | runExceptionalT m >>= \ a -> |

case a of |
case a of |
||

− | Exception l -> runExActionT (h l) |
+ | Exception l -> runExceptionalT (h l) |

Success r -> return (Success r) |
Success r -> return (Success r) |
||

bracketT :: Monad m => |
bracketT :: Monad m => |
||

− | ExActionT e m h -> |
+ | ExceptionalT e m h -> |

− | (h -> ExActionT e m ()) -> |
+ | (h -> ExceptionalT e m ()) -> |

− | (h -> ExActionT e m a) -> |
+ | (h -> ExceptionalT e m a) -> |

− | ExActionT e m a |
+ | ExceptionalT e m a |

bracketT open close body = |
bracketT open close body = |
||

open >>= (\ h -> |
open >>= (\ h -> |
||

− | ExActionT $ |
+ | ExceptionalT $ |

− | do a <- runExActionT (body h) |
+ | do a <- runExceptionalT (body h) |

− | runExActionT (close h) |
+ | runExceptionalT (close h) |

return a) |
return a) |
||

</haskell> |
</haskell> |
||

Line 90: | Line 90: | ||

deriving (Show, Eq, Enum) |
deriving (Show, Eq, Enum) |
||

− | open :: FilePath -> ExActionT IOException IO Handle |
+ | open :: FilePath -> ExceptionalT IOException IO Handle |

− | close :: Handle -> ExActionT IOException IO () |
+ | close :: Handle -> ExceptionalT IOException IO () |

− | read :: Handle -> ExActionT IOException IO String |
+ | read :: Handle -> ExceptionalT IOException IO String |

− | write :: Handle -> String -> ExActionT IOException IO () |
+ | write :: Handle -> String -> ExceptionalT IOException IO () |

− | readText :: FilePath -> ExActionT IOException IO String |
+ | readText :: FilePath -> ExceptionalT IOException IO String |

readText fileName = |
readText fileName = |
||

bracketT (open fileName) close $ \h -> |
bracketT (open fileName) close $ \h -> |
||

Line 108: | Line 108: | ||

main :: IO () |
main :: IO () |
||

main = |
main = |
||

− | do result <- runExActionT (readText "test") |
+ | do result <- runExceptionalT (readText "test") |

case result of |
case result of |
||

Exception e -> putStrLn ("When reading file 'test' we encountered exception " ++ show e) |
Exception e -> putStrLn ("When reading file 'test' we encountered exception " ++ show e) |

## Revision as of 22:52, 7 February 2009

An **exception** denotes an unpredictable situation at runtime, like "out of disk storage", "read protected file", "user removed disk while reading", "syntax error in user input".
These are situation which occur relatively seldom and thus their immediate handling would clutter the code which should describe the regular processing.
Since exceptions must be expected at runtime there are also mechanisms for (selectively) handling them.

In general you should be very careful, not to mix up exceptions with errors. Actually, an unhandled exception is an error.

## 1 Implementation

The great thing about Haskell is, that it is not necessary to hard-wire the exception handling into the language. Everything is already there to implement definition and handling of exceptions nicely.

See the implementation inThere is an old dispute between C++ programmers on whether exceptions or error return codes are the right way. Also Niklaus Wirth considered exceptions to be the reincarnation of GOTO and thus omitted them in his languages. Now Haskell solves the problem the diplomatic way: Function return error codes, but the handling of error codes does not uglify the calling code.

First we implement exception handling for non-monadic functions. Since no IO functions are involved, we can still not handle exceptional situations induced from outside the world, but we can handle situations, where it is unacceptable for the caller to check a priori whether the call can succeed.

data Exceptional e a = Success a | Exception e deriving (Show) instance Monad (Exceptional e) where return = Success Exception l >>= _ = Exception l Success r >>= k = k r throw :: e -> Exceptional e a throw = Exception catch :: Exceptional e a -> (e -> Exceptional e a) -> Exceptional e a catch (Exception l) h = h l catch (Success r) _ = Success r

Now we extend this to monadic functions.

This is not restricted to IO, but may be used immediately also for non-deterministic algorithms implemented with thenewtype ExceptionalT e m a = ExceptionalT {runExceptionalT :: m (Exceptional e a)} instance Monad m => Monad (ExceptionalT e m) where return = ExceptionalT . return . Success m >>= k = ExceptionalT $ runExceptionalT m >>= \ a -> case a of Exception e -> return (Exception e) Success r -> runExceptionalT (k r) throwT :: Monad m => e -> ExceptionalT e m a throwT = ExceptionalT . return . Exception catchT :: Monad m => ExceptionalT e m a -> (e -> ExceptionalT e m a) -> ExceptionalT e m a catchT m h = ExceptionalT $ runExceptionalT m >>= \ a -> case a of Exception l -> runExceptionalT (h l) Success r -> return (Success r) bracketT :: Monad m => ExceptionalT e m h -> (h -> ExceptionalT e m ()) -> (h -> ExceptionalT e m a) -> ExceptionalT e m a bracketT open close body = open >>= (\ h -> ExceptionalT $ do a <- runExceptionalT (body h) runExceptionalT (close h) return a)

Here are some examples for typical IO functions with explicit exceptions.

data IOException = DiskFull | FileDoesNotExist | ReadProtected | WriteProtected | NoSpaceOnDevice deriving (Show, Eq, Enum) open :: FilePath -> ExceptionalT IOException IO Handle close :: Handle -> ExceptionalT IOException IO () read :: Handle -> ExceptionalT IOException IO String write :: Handle -> String -> ExceptionalT IOException IO () readText :: FilePath -> ExceptionalT IOException IO String readText fileName = bracketT (open fileName) close $ \h -> read h

Finally we can escape from the Exception monad if we handle the exceptions completely.

main :: IO () main = do result <- runExceptionalT (readText "test") case result of Exception e -> putStrLn ("When reading file 'test' we encountered exception " ++ show e) Success x -> putStrLn ("Content of the file 'test'\n" ++ x)