MonadCompose 0.6.0.0 → 0.7.0.0
raw patch · 3 files changed
+29/−43 lines, 3 files
Files
- Control/Monad/IOT.hs +18/−34
- Control/Monad/Lifter.hs +5/−1
- MonadCompose.cabal +6/−8
Control/Monad/IOT.hs view
@@ -4,14 +4,14 @@ import GHC.IO hiding (liftIO) import GHC.Prim -import Control.Monad.Trans -- (MonadIO(..)) +import Control.Monad.Trans (MonadIO(..)) import Control.Monad.Identity --- import Control.Monad.Morph +import Control.Monad.Morph import Control.Monad import Control.Applicative import Unsafe.Coerce -data St = St { unSt :: !(State# RealWorld) } +data Ret a = Ret (State# RealWorld) a -- | An IO monad transformer. -- @@ -24,33 +24,20 @@ -- -- Should be integrated with STT. -class MFunctor t where - {-| Lift a monad morphism from @m@ to @n@ into a monad morphism from - @(t m)@ to @(t n)@ - -} - hoist :: (Monad m) => (forall a . m a -> n a) -> t m b -> t n b - -class (MFunctor t, MonadTrans t) => MMonad t where - {-| Embed a newly created 'MMonad' layer within an existing layer - - 'embed' is analogous to ('=<<') - -} - embed :: (Monad n) => (forall a . m a -> t n a) -> t m b -> t n b - data Sequence m where None :: Sequence m - Seq :: (Monad m) => IO St -> Sequence (IOT m) + Seq :: (Monad m) => IO (Ret ()) -> Sequence (IOT m) -{-# INLINE runSequence #-} -runSequence :: (Monad m) => Sequence m -> St -> m St -runSequence None = return -runSequence (Seq io) = \_ -> liftIO io +{-# NOINLINE runSequence #-} +runSequence :: (Monad m) => Sequence m -> State# RealWorld -> m (Ret ()) +runSequence None s = return (Ret s ()) +runSequence (Seq io) _ = liftIO io -newtype IOT m t = IOT (Sequence m -> St -> m (St, t)) +newtype IOT m t = IOT (Sequence m -> State# RealWorld -> m (Ret t)) instance (Monad m) => Monad (IOT m) where - return x = IOT (\_ s -> return (s, x)) - IOT f >>= g = IOT (\i s -> f i s >>= \(s2, x) -> case g x of + return x = IOT (\_ s -> return (Ret s x)) + IOT f >>= g = IOT (\i s -> f i s >>= \(Ret s2 x) -> case g x of IOT h -> h i s2) instance (Monad m) => Applicative (IOT m) where @@ -61,28 +48,25 @@ fmap f m = m >>= return . f instance (Monad m) => MonadIO (IOT m) where - liftIO (IO f) = IOT (\_ s -> case f (unSt s) of - (# s2, x #) -> return (St s2, x)) + liftIO (IO f) = IOT (\_ s -> case f s of + (# s2, x #) -> return (Ret s2 x)) instance MonadTrans IOT where - lift m = IOT (\i s -> m >>= \x -> liftM (\s -> (s, x)) (runSequence i s)) + lift m = IOT (\i s -> m >>= \x -> liftM (\(Ret s ()) -> Ret s x) (runSequence i s)) -- Flatten two layers into one. mmorph exports 'squash'. -- -- Unsafely interleave actions in the outer monad, but sequence with the -- inner monad using a sequencing fn. -_squash (IOT f) = IOT (\i s -> let IOT g = f (Seq $ IO $ \s -> (# s, St s #)) s in g i s >>= return . snd) +_squash (IOT f) = IOT (\i s -> let IOT g = f (Seq $ IO $ \s -> (# s, Ret s () #)) s in g i s >>= \(Ret _ pr) -> return pr) _hoist :: (forall t. m t -> n t) -> IOT m t -> IOT n t -_hoist f (IOT g) = IOT (\i -> f . g (unsafeCoerce i)) +_hoist f (IOT g) = IOT (\i s -> f (g (unsafeCoerce i) s)) -- Type safety proof: the datum i is either in None or Seq. -- * If it is in None, it is valid at all types. -- * If it is in Seq, the only way it can be projected is from IOT m to IO -- and back again. liftIO is valid at both. So 'runSequence' will -- certainly be used at a valid type. --- --- Here is the test of where things can go wrong: -test = run $ _squash $ hoist (liftIO . run) $ liftIO (print "A") >> lift (liftIO (print "B")) instance MMonad IOT where embed f = _squash . _hoist f @@ -92,5 +76,5 @@ -- | Run an IOT. run :: IOT Identity t -> IO t -run (IOT f) = IO (\s -> case runIdentity (f None (St s)) of - (s2, x) -> (# unSt s2, x #)) +run (IOT f) = IO (\s -> case runIdentity (f None s) of + Ret s2 x -> (# s2, x #))
Control/Monad/Lifter.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverlappingInstances, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, UndecidableInstances #-} +{-# LANGUAGE OverlappingInstances, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, UndecidableInstances, TypeOperators #-} module Control.Monad.Lifter where @@ -6,6 +6,7 @@ import Control.Monad.ST import Control.Monad.Identity import Control.Monad.Morph +import Control.Monad.PlusMonad -- | An automatic lifter. The idea of automatic lifting is due to Dan Piponi. class Lifter m n where @@ -22,6 +23,9 @@ instance Lifter Identity Identity where lf = id + +instance Lifter m (m ::+ n) where + lf = inl instance (MonadTrans n, Monad x, Lifter m x) => Lifter m (n x) where lf = lift . lf
MonadCompose.cabal view
@@ -1,15 +1,13 @@ name: MonadCompose -version: 0.6.0.0 +version: 0.7.0.0 synopsis: Methods for composing monads. description: Methods for composing monads. - - The IO monad transformer solves the problem of combining two IO-performing monad transformers, so that neither one needs to provide a MonadIO interface, and both can be transformed separately. - + . + The IO monad transformer solves the problem of combining two IO-performing monads, so that neither one needs to provide a MonadIO interface and both can be transformed separately. + . Most known monads have a distributive law. The Distributive module implements distributivity for monad transformers. - - A monad transformer can transform another monad, but if you have two monads both lacking a transformer, there is little you can do in general. However, you can compose them in a coproduct construction. The PlusMonad module implements a similar plan, but differs from coproducts in that it doesn't compress together contiguous uses of a monad. Another mystery is how to get the other distributive law (m(x + y) -> mx + my). - - I would like the auto-lifter and the Plus monad to work together, but I can't figure out how to coax IncoherentInstances to support it. + . + A monad transformer can transform another monad, but if you have two monads both lacking a transformer, there is little you can do in general. However, you can compose them in a coproduct construction. The PlusMonad module implements a similar plan, but differs from coproducts in that it doesn't compress together contiguous uses of a monad. homepage: http://alkalisoftware.net license: BSD3 license-file: LICENSE