
Brian Hulley wrote:
I've started work on a module to replace Control.Exception by wrapping all the original Control.Exception functions in more general monadic functions and using two type classes as follows:
class MonadIO m => MonadException m where catch :: m a -> (Exception -> m a) -> m a catchDyn :: Typeable exception => m a -> (exception -> m a) -> m a catchJust :: (Exception -> Maybe b) -> m a -> (b -> m a) -> m a try :: m a -> m (Either Exception a) tryJust :: (Exception -> Maybe b) -> m a -> m (Either b a)
and
class MonadIO m => MonadIOU m where getUnliftIO :: m (m a -> IO a)
All the other functions can be implemented just using MonadIO or MonadIOU or MonadException in place of IO (depending on the function eg bracket needs MonadIOU) - just in case anyone is interested.
After more thought, it seems that it *should* be possible to implement block and unblock for StateT monads under certain conditions, using a different unlift function to return IO (a,s) instead of just IO a. Therefore I've changed things around, and also by looking at the source code for the current Control.Exception module, arrived at the following revised design (I've implemented all the other functions in terms of the classes below) class MonadIO m => MonadException m where catch :: m a -> (Exception -> m a) -> m a catchDyn :: Typeable exception => m a -> (exception -> m a) -> m a block, unblock :: MonadException m => m a -> m a class MonadIO m => MonadIOU m where getUnliftIO :: m (m a -> IO a) However I then want to say that any instance of MonadIOU is also an instance of MonadException. I tried: instance MonadIOU m => MonadException m where catch action e_m = do unliftIOa <- getUnliftIO unliftIOb <- getUnliftIO liftIO $ C.catch (unliftIOa action) (\e -> unliftIOb(e_m e)) -- etc but this only compiles with -fallow-undecidable-instances. I'm puzzled at why there is a problem with such a simple instance declaration, and also don't know if this means my design is fatally flawed. The highlight of the above design is that all that's needed for many monads such as MonadIOU m => ReaderT r m is a definition of the one unlifting function, but it also allows instances of MonadException to be declared where the monad (eg a StateT s m) doesn't support this particular operation. Any ideas? Thanks, Brian.