Move MonadIO to base

Bas van Dijk v.dijk.bas at
Sun Apr 11 14:48:44 EDT 2010

On Sun, Apr 11, 2010 at 5:35 PM, Yitzchak Gale <gale at> wrote:
> Twan van Laarhoven wrote:
>> If MonadIO were in base, then the base library itself could also use it. For
>> example the functions in System.IO could be lifted to work on any MonadIO
>> monad. Whether that is a good idea is completely orthogonal to this
>> discussion, however.
> The main problem is that exceptions don't work well with
> MonadIO in GHC. So really MonadIO is currently only a toy and
> cannot be used in production code.
> The reason for this is that just about any operation involving exceptions
> ultimately depends (via the source code of base library functions)
> on the function
> block :: IO a -> IO a
> and that type is hard-wired in a GHC primitive.

Can you explain why this is a problem?

Do note we have the packages MonadCatchIO-transformers[1] and
MonadCatchIO-mtl[2] that both provide:

class MonadIO  m => MonadCatchIO  m where
    catch :: Exception e => m a -> (e -> m a) -> m a
    block :: m a -> m a
    unblock :: m a -> m a

> Additional primitives to support things like
> startBlocking :: IO ()
> stopBlocking :: IO ()
> which would play well with MonadIO, could be added to
> GHC, but they would lose important optimizations.
> I'm not sure about the order of magnitude of the cost - whether
> it would just make things run more slowly, or render them
> completely unusable. If the former, I am in favor of this
> proposal, but only combined with the addition of GHC support
> for startBlocking and stopBlocking. If the latter, then
> there is no point to this proposal.

Note that currently a discussion[3] is going on about hiding 'block'
and 'unblock' and replacing them with:

mask :: ((IO a -> IO a) -> IO b) -> IO b
mask io = do
   b <- blocked
   if b
      then io id
      else block $ io unblock

to be used like this:

a `finally` b =
   mask $ \restore -> do
     r <- restore a `onException` b
     return r

Of course when this change is made the MonadCatchIO class has to be
adapted to something like:

class MonadIO m => MonadCatchIO m where
    catch :: Exception e => m a -> (e -> m a) -> m a
    mask :: ((m b -> m b) -> m a) -> m a

instance MonadCatchIO IO where
    catch = Control.Exception.catch

    mask io = do
      b <- blocked
      if b
        then io id
        else block $ io unblock

instance MonadCatchIO m => MonadCatchIO (ReaderT r m) where
    m `catch` f = ReaderT $ \r -> runReaderT m r `catch`
                                    \e -> runReaderT (f e) r

    mask io = ReaderT inner
          inner r = mask innerIO
                innerIO innerRestore = runReaderT (io restore) r
                      restore reader = ReaderT $ innerRestore .
runReaderT reader

Note I'm +1 for separating MonadIO from transformers and mtl. I'm not
sure yet about putting it in base.




More information about the Libraries mailing list