Exception
From HaskellWiki
(exception implementation for non-monadic functions) |
(monad transformer) |
||
| Line 14: | Line 14: | ||
See the implementation in <hask>Control.Monad.Error</hask> (and please, excuse the misleading name, for now). | See the implementation in <hask>Control.Monad.Error</hask> (and please, excuse the misleading name, for now). | ||
| + | First for non-monadic functions. | ||
<haskell> | <haskell> | ||
| - | data | + | data ExAction e a = |
Success a | Success a | ||
| Exception e | | Exception e | ||
| + | deriving (Show) | ||
| - | instance Monad ( | + | instance Monad (ExAction e) where |
return = Success | return = Success | ||
Exception l >>= _ = Exception l | Exception l >>= _ = Exception l | ||
| - | + | Success r >>= k = k r | |
| - | throw :: e -> | + | throw :: e -> ExAction e a |
throw = Exception | throw = Exception | ||
| - | catch :: | + | catch :: ExAction e a -> (e -> ExAction e a) -> ExAction e a |
catch (Exception l) h = h l | catch (Exception l) h = h l | ||
catch (Success r) _ = Success r | catch (Success r) _ = Success r | ||
</haskell> | </haskell> | ||
| - | < | + | Now we extend this monadic functions. |
| - | + | This is not restricted to IO, but may also immediately used for non-deterministic algorithms implemented with <hask>List</hask> monad. | |
| - | + | <haskell> | |
| - | + | newtype ExActionT e m a = | |
| - | + | ExActionT {runExActionT :: m (ExAction e a)} | |
| - | --> | + | |
| + | instance Monad m => Monad (ExActionT e m) where | ||
| + | return = ExActionT . return . Success | ||
| + | m >>= k = ExActionT $ | ||
| + | runExActionT m >>= \ a -> | ||
| + | case a of | ||
| + | Exception e -> return (Exception e) | ||
| + | Success r -> runExActionT (k r) | ||
| + | |||
| + | throwT :: Monad m => e -> ExActionT e m a | ||
| + | throwT = ExActionT . return . Exception | ||
| + | |||
| + | catchT :: Monad m => | ||
| + | ExActionT e m a -> (e -> ExActionT e m a) -> ExActionT e m a | ||
| + | catchT m h = ExActionT $ | ||
| + | runExActionT m >>= \ a -> | ||
| + | case a of | ||
| + | Exception l -> runExActionT (h l) | ||
| + | Success r -> return (Success r) | ||
| + | |||
| + | bracketT :: Monad m => | ||
| + | ExActionT e m h -> | ||
| + | (h -> ExActionT e m ()) -> | ||
| + | (h -> ExActionT e m a) -> | ||
| + | ExActionT e m a | ||
| + | bracketT open close body = | ||
| + | open >>= (\ h -> | ||
| + | ExActionT $ | ||
| + | do a <- runExActionT (body h) | ||
| + | runExActionT (close h) | ||
| + | return a) | ||
| + | </haskell> | ||
| + | |||
| + | |||
| + | <haskell> | ||
| + | data IOException = | ||
| + | DiskFull | ||
| + | | FileDoesNotExist | ||
| + | | ReadProtected | ||
| + | | WriteProtected | ||
| + | | NoSpaceOnDevice | ||
| + | deriving (Show, Eq, Enum) | ||
| + | |||
| + | open :: FilePath -> ExActionT IOException IO Handle | ||
| + | |||
| + | close :: Handle -> ExActionT IOException IO () | ||
| + | |||
| + | read :: Handle -> ExActionT IOException IO String | ||
| + | |||
| + | write :: Handle -> String -> ExActionT IOException IO () | ||
| + | |||
| + | readText :: FilePath -> ExActionT IOException IO String | ||
| + | readText fileName = | ||
| + | bracketT (open fileName) close $ \h -> | ||
| + | read h | ||
| + | </haskell> | ||
| + | |||
== See also == | == See also == | ||
Revision as of 15:00, 23 January 2008
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 inFirst for non-monadic functions.
data ExAction e a = Success a | Exception e deriving (Show) instance Monad (ExAction e) where return = Success Exception l >>= _ = Exception l Success r >>= k = k r throw :: e -> ExAction e a throw = Exception catch :: ExAction e a -> (e -> ExAction e a) -> ExAction e a catch (Exception l) h = h l catch (Success r) _ = Success r
Now we extend this monadic functions.
This is not restricted to IO, but may also immediately used for non-deterministic algorithms implemented withnewtype ExActionT e m a = ExActionT {runExActionT :: m (ExAction e a)} instance Monad m => Monad (ExActionT e m) where return = ExActionT . return . Success m >>= k = ExActionT $ runExActionT m >>= \ a -> case a of Exception e -> return (Exception e) Success r -> runExActionT (k r) throwT :: Monad m => e -> ExActionT e m a throwT = ExActionT . return . Exception catchT :: Monad m => ExActionT e m a -> (e -> ExActionT e m a) -> ExActionT e m a catchT m h = ExActionT $ runExActionT m >>= \ a -> case a of Exception l -> runExActionT (h l) Success r -> return (Success r) bracketT :: Monad m => ExActionT e m h -> (h -> ExActionT e m ()) -> (h -> ExActionT e m a) -> ExActionT e m a bracketT open close body = open >>= (\ h -> ExActionT $ do a <- runExActionT (body h) runExActionT (close h) return a)
data IOException = DiskFull | FileDoesNotExist | ReadProtected | WriteProtected | NoSpaceOnDevice deriving (Show, Eq, Enum) open :: FilePath -> ExActionT IOException IO Handle close :: Handle -> ExActionT IOException IO () read :: Handle -> ExActionT IOException IO String write :: Handle -> String -> ExActionT IOException IO () readText :: FilePath -> ExActionT IOException IO String readText fileName = bracketT (open fileName) close $ \h -> read h
