There are too many error handling conventions used in library code!

Robert Dockins robdockins at fastmail.fm
Sun Mar 11 12:34:43 EDT 2007


On Sunday 11 March 2007 11:22, Robert Dockins wrote:
[snip]

> One option that strikes me as a possibility is to add an (Error Dynamic)
> instance and use (MonadError Dynamic).  This has the advantages of
> 'throwDyn' without tying it to the IO monad.  Together with a suite of
> lifting functions, you could (as a library consumer) integrate almost any
> error handling convention into a single monad, leaving the ugliness of
> handling various error representations to the error handling code, where it
> belongs.

[snip]

Here's a quick and dirty implementation for discussion. It compiles, but I 
haven't tested it.

Rob Dockins


----------------------------------------------------------

{-# OPTIONS -fglasgow-exts #-}

module ErrorDyn
( ErrorDyn
, ErrorDynT
, liftMaybe
, liftEither
, liftErrorT
, liftWriter
, liftWriterT
, handleDynErr
, catchDynErr
, runErrorDynT
, execErrorDynT
, runErrorDyn
, execErrorDyn
) where

import Data.Monoid
import Data.Typeable
import Data.Dynamic
import Control.Monad
import Control.Monad.Identity
import Control.Monad.Error
import Control.Monad.Writer

instance Error Dynamic where
  noMsg      = toDyn ()
  strMsg str = toDyn str

type ErrorDyn = ErrorDynT Identity

newtype ErrorDynT m a = EDT { unEDT :: ErrorT Dynamic m a }
  deriving (Monad, MonadTrans)

runErrorDynT :: Monad m => ErrorDynT m a -> m (Either Dynamic a)
runErrorDynT = runErrorT . unEDT

execErrorDynT :: Monad m => ErrorDynT m a -> m a
execErrorDynT m = runErrorDynT m >>= either unhdl return
  where unhdl dyn = fail $ "Unhandled dynamic error of type: "++(show dyn)

runErrorDyn :: ErrorDyn a -> Either Dynamic a
runErrorDyn = runIdentity . runErrorDynT

execErrorDyn :: ErrorDyn a -> a
execErrorDyn = runIdentity . execErrorDynT

instance Monad m => MonadError Dynamic (ErrorDynT m) where
  throwError e   = EDT (throwError e)
  catchError m f = EDT (catchError (unEDT m) (unEDT . f))

liftMaybe :: (Monad m) => Maybe a -> ErrorDynT m a
liftMaybe = maybe (throwError noMsg) return

liftEither :: (Monad m, Typeable e) => Either e a -> ErrorDynT m a
liftEither = either (throwError . toDyn) return

liftErrorT :: (Monad m, Typeable e) => ErrorT e m a -> ErrorDynT m a
liftErrorT m = lift (runErrorT m) >>= liftEither


liftWriter :: (Monad m, Eq e, Monoid e, Typeable e)
           => Writer e a
           -> ErrorDynT m a
liftWriter m = do
   let (a, w) = runWriter m
   when (w /= mempty) (throwError (toDyn w))
   return a


liftWriterT :: (Monad m, Eq e, Monoid e, Typeable e)
            => WriterT e m a
            -> ErrorDynT m a
liftWriterT m = do
   (a, w) <- lift (runWriterT m)
   when (w /= mempty) (throwError (toDyn w))
   return a


handleDynErr :: (Monad m, Typeable e)
             => (e -> ErrorDynT m a)
             -> ErrorDynT m a
             -> ErrorDynT m a
handleDynErr f m = catchError m hdl
 where
  hdl e = maybe (throwError e) f (fromDynamic e)


catchDynErr :: (Monad m, Typeable e)
            => ErrorDynT m a
            -> (e -> ErrorDynT m a)
            -> ErrorDynT m a
catchDynErr = flip handleDynErr


More information about the Libraries mailing list