Personal tools


From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(darcs repository)

Revision as of 14:53, 25 September 2008


) Unfortunately Haskell's standard library names common exceptions of IO actions
and the module
is about exception handling not error handling.


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 in
(and please, excuse the misleading name, for now).

There 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 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 to monadic functions.

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)

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

data IOException =
   | 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


main :: IO ()
main =
   do result <- runExActionT (readText "test")
      case result of

2 See also