
On Sun, Apr 11, 2010 at 5:35 PM, Yitzchak Gale
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