
5 Sep
2003
5 Sep
'03
5:59 p.m.
hi, Simon Marlow wrote: >>See the attached patch for the details. >> >>This solution is maybe a bit ugly, since these methods are fairly >>specific (liftIO' is needed to generalize block and unblock, and >>liftIO'' is needed to generalize catchException). >> >>But it does allow one to use catch/bracket/etc with monads built on >>top of IO with monad transformers, which is quite nice: > > > Thanks. I think I would prefer to have the generalised versions of > block/unblock/catchException separate from the IO-specific versions, and > exported by one of the Control.Monad modules, to avoid breaking too much > code, and to avoid wiring MonadIO in too deeply. > > Would you mind redoing the patch? > > Iavor: how do these changes sit with your redesign of the monad stuff? well the patch does two things: 1. adds 2 new methods to the MonadIO class: liftIO' :: MonadIO m => (forall a. IO a -> IO a) -> m a -> m a liftIO'' :: MonadIO m => (forall a. IO a -> (b -> IO a) -> IO a) -> m a -> (b -> m a) -> m a 2. using these methods redefines the IO exception primitives in the "new" library there is no MonadIO class, it was generalized to HasBaseMonad m n | m -> n, so now one can perform operations directly in the base monad for any base monad not just IO. it is easy to add liftIO' to the HasBaseMonadClass, it essentially repeatedly applies 'mapTrans' until the base monad is reached (and there is a bit of an adhoc implementtaion for continuations as they don't have mapTrans operation i copied that from the patch). so now we have: mapBase :: HasBaseMonad m n => (forall a. n a -> n a) -> m a -> m a liftIO'' seems a bit adhoc though, and i can't quite see where to fit it. suggestions on what to do with it are welcome. to me it looks like the lifting of 'catch' with exceptions of type b and it seems that's how it is used in the patch. in the library there already are such liftings, to lift the method 'handle' (ex catchError) of the MonadError class (it has the same type as 'catch'). it is unfortunate that there are so many different 'catch' and 'handle' methods, and for a long time i have been wondering if we should simply make IO an instance of the MonadError class. then there is the question of what to do if one wraps an ErrorT transformer around IO. in such a situation one may want to throw two different kinds of exceptions but with the current class structure that doesn't work, as the transformer's methods will "shadow" the IO ones. there seem to be a few different ways to work around this, but none of them seem too great: 1. one could use a more complicated class structure as i did in my old monadic library, and then one can specify which error they are throwing or catching, i.e. there was a way to "name" different transformers of the same type (see www.cse.ogi.edu/~diatchki for details). this however was kind of clunky and there may be some language extension to make it easier to use, but it is probbaly not very practical as it is. 2. one can have a special way of lifting IO operations to monads that already have errors that re-thrpw the errros, e.g. something like: liftIO :: (HasBaseMonad m IO, MonadError e m) => (Exception -> e) -> IO a -> m a liftIO cvt m = do x <- inBase (try m) case x of Left err -> raise (cvt err) Right a -> return a any other ideas? bye iavor -- ================================================== | Iavor S. Diatchki, Ph.D. student | | Department of Computer Science and Engineering | | School of OGI at OHSU | | http://www.cse.ogi.edu/~diatchki | ==================================================