Move MonadIO to base

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


On Sun, Apr 11, 2010 at 5:35 PM, Yitzchak Gale <gale at sefer.org> 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
     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
        where
          inner r = mask innerIO
              where
                innerIO innerRestore = runReaderT (io restore) r
                    where
                      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.

regards,

Bas

[1] http://hackage.haskell.org/package/MonadCatchIO-transformers
[2] http://hackage.haskell.org/package/MonadCatchIO-mtl
[3] http://thread.gmane.org/gmane.comp.lang.haskell.cafe/72145/focus=72182


More information about the Libraries mailing list