MonadCompose 0.8.2.0 → 0.8.3.0
raw patch · 6 files changed
+213/−120 lines, 6 filesdep +comonaddep +monad-loopsdep +parallel
Dependencies added: comonad, monad-loops, parallel, transformers-compat
Files
- Control/Linear.hs +126/−70
- Control/Monad/Distributive.hs +2/−2
- Control/Monad/IOT.hs +78/−44
- Control/Monad/Lifter.hs +1/−1
- Control/Monad/PlusMonad.hs +3/−1
- MonadCompose.cabal +3/−2
Control/Linear.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE Rank2Types, ScopedTypeVariables, MagicHash, UnboxedTuples, FlexibleInstances, GeneralizedNewtypeDeriving, NoMonomorphismRestriction #-} +{-# LANGUAGE Trustworthy, Rank2Types, ScopedTypeVariables, MagicHash, UnboxedTuples, FlexibleInstances, GeneralizedNewtypeDeriving, NoMonomorphismRestriction #-} -- | A linear type-based I/O system a la Clean - including a "safe C" (like Cyclone). -- -- This is an alternative to composing monads - one can decompose them into their @@ -6,7 +6,7 @@ -- (See Kieburtz, http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.46.5169&rep=rep1&type=pdf) module Control.Linear (St, A, Blank, Pair, Fn, (>>==), rtn, -- * Algebraic operations -run, bimap, assoc1, assoc2, drop1, drop2, undrop1, undrop2, swap, apply, curry, distr, void', bimap', +run, bimap, assoc1, assoc2, drop1, drop2, undrop1, undrop2, swap, apply, curry, distr, assoc3, assoc4, void', bimap', -- * Basic I/O system Exclusive, Semiclosed, Open, Placeholder(Placeholder), open, getStdin, getStdout, getStderr, close, close1, fileSize, setFileSize, eof, seek, tell, char, line, lookahead, contents, putC, putS, random, -- * Safe pointer facilities @@ -16,12 +16,17 @@ -- ** Strong update peek', poke', changeType, -- ** Operations on nonlinear data / Weak update -newNonlinear, peek1, poke1 +newNonlinear, peek1, poke1, +-- * Multithreading +fork, join', +-- * Example programs +helloWorld, printStuff, concurrent ) where import Control.Arrow import Control.Category import Control.Monad +import Control.Parallel import GHC.Prim import GHC.IO import GHC.Base (realWorld#) @@ -35,7 +40,7 @@ import Data.Default import Data.Int import Prelude hiding (id, (.), curry) -import System.Random (getStdGen) +import System.Random hiding (split, random) import System.IO.Unsafe -- * Linear type machinery @@ -50,11 +55,11 @@ data Blank = Blank -data Pair t u = Pair !t !u +data Pair t u = Pair t u instance (Default a) => Category (A a) where - id = A (\x -> (x, def)) - A f . A g = A (f . fst . g) + id = rtn def + a . a2 = a2 >>== \(_ :: a) -> a instance (Default a) => Arrow (A a) where arr f = A (\x -> (f x, def)) @@ -69,64 +74,98 @@ infixl 1 >>== -- | Monadic bind (for nonlinear data). -A f >>== g = A (\x -> let - (y, z) = f x - A h = g z in - h y) +{-# INLINE[0] (>>==) #-} +A f >>== g = A (\x -> case f x of + (y, z) -> case g z of + A h -> h y) -- | Monadic return +{-# INLINE[0] rtn #-} rtn x = A (\y -> (y, x)) -- | This setup is from http://cs.ioc.ee/~tarmo/tsem11/jeltsch1602-slides.pdf -- -- It implements some of http://pauillac.inria.fr/~fpottier/slides/fpottier-2007-05-linear-bestiary.pdf -{-# INLINE run #-} +{-# INLINE[0] run #-} run :: A a St St -> IO a run (A f) = IO $ \world -> case f (St world) of (St world', x) -> (# world', x #) -{-# INLINE bimap #-} +{-# INLINE[0] bimap #-} bimap (A f) (A g) = A (\(Pair a b) -> let (c, d) = f a (e, h) = g b in - (Pair c e, (d, h))) + (c `par` e) `seq` (Pair c e, (d, h))) -{-# INLINE assoc1 #-} +{-# INLINE[0] assoc1 #-} assoc1 = A (\(Pair (Pair a b) c) -> (Pair a (Pair b c), ())) -{-# INLINE assoc2 #-} +{-# INLINE[0] assoc2 #-} assoc2 = A (\(Pair a (Pair b c)) -> (Pair (Pair a b) c, ())) -{-# INLINE drop1 #-} +{-# INLINE[0] drop1 #-} drop1 = A (\(Pair Blank x) -> (x, ())) -{-# INLINE drop2 #-} +{-# INLINE[0] drop2 #-} drop2 = A (\(Pair x Blank) -> (x, ())) -{-# INLINE undrop1 #-} +{-# INLINE[0] undrop1 #-} undrop1 = A (\x -> (Pair Blank x, ())) -{-# INLINE undrop2 #-} +{-# INLINE[0] undrop2 #-} undrop2 = A (\x -> (Pair x Blank, ())) -{-# INLINE swap #-} +{-# INLINE[0] swap #-} swap = A (\(Pair x y) -> (Pair y x, ())) -{-# INLINE apply #-} +{-# INLINE[0] apply #-} apply = A (\(Pair (A f) x) -> f x) -{-# INLINE curry #-} +{-# INLINE[0] curry #-} curry (A f) = A (\x -> (A (\y -> f (Pair x y)), ())) -{-# INLINE distr #-} +{-# INLINE[0] distr #-} distr = A (\(Pair a ei) -> (either (Left . Pair a) (Right . Pair a) ei, ())) +{-# INLINE[0] assoc3 #-} +assoc3 ((x, y), z) = (x, (y, z)) + +{-# INLINE[0] assoc4 #-} +assoc4 (x, (y, z)) = ((x, y), z) + +{-# RULES +"assoc"[1] forall a a2 a3. assoc1 >>== \_ -> bimap a (bimap a2 a3) = bimap (bimap a a2) a3 >>== \x -> assoc1 >>== \_ -> rtn (assoc3 x) +"assoc2"[1] forall a a2 a3. assoc2 >>== \_ -> bimap (bimap a a2) a3 = bimap a (bimap a2 a3) >>== \x -> assoc2 >>== \_ -> rtn (assoc4 x) +"drop"[1] forall a. drop1 >>== \_ -> a = bimap id a >>== \((), x) -> drop1 >>== \_ -> rtn x +"drop2"[1] forall a. drop2 >>== \_ -> a = bimap a id >>== \(x, ()) -> drop2 >>== \_ -> rtn x +"swap"[1] forall a a2. swap >>== \_ -> bimap a a2 = bimap a2 a >>== \(x, y) -> swap >>== \_ -> rtn (y, x) +"fuseMaps"[1] forall a a2 a3 a4. bimap a3 a4 >>== \_ -> bimap a a2 + = bimap (a3 >>== \_ -> a) (a4 >>== \_ -> a2) +"fuseMaps2"[1] forall a a2 a3 a4. bimap a3 a4 >>== \(x, y) -> bimap (a x) (a2 y) + = bimap (a3 >>== a) (a4 >>== a2) +"fuseMaps3"[1] forall a a2 a3 a4 a5. bimap a3 a4 >>== \_ -> bimap a a2 >>== a5 + = bimap (a3 >>== \_ -> a) (a4 >>== \_ -> a2) >>== a5 +"fuseMaps4"[1] forall a a2 a3 a4 a5. bimap a3 a4 >>== \(x, y) -> bimap (a x) (a2 y) >>== a5 + = bimap (a3 >>== a) (a4 >>== a2) >>== a5 +"monad"[1] forall x a. rtn x >>== a = a x +"monad2"[1] forall a a2 a3. a >>== a2 >>== a3 = a >>== \x -> a2 x >>== a3 +"drop3"[1] drop1 >>== \_ -> undrop1 = id +"drop4"[1] drop2 >>== \_ -> undrop2 = id +"drop5"[1] undrop1 >>== \_ -> drop1 = id +"drop6"[1] undrop2 >>== \_ -> drop2 = id +"assoc3"[1] assoc1 >>== \_ -> assoc2 = id +"assoc4"[1] assoc2 >>== \_ -> assoc1 = id +"swap2"[1] swap >>== \_ -> swap = id +"inlinearrow" forall a a2. a >>> a2 = a2 . a +"inlinecompose" forall a a2. a . a2 = a2 >>== \_ -> a + #-} + ------------------------------------------------------ -{-# INLINE void' #-} +{-# INLINE[2] void' #-} void' = (>>== const (rtn ())) -{-# INLINE bimap' #-} +{-# INLINE[2] bimap' #-} bimap' :: A () t u -> A () v w -> A () (Pair t v) (Pair u w) bimap' a a2 = void' (bimap a a2) @@ -186,10 +225,12 @@ putS s = lift (\h -> hPutStr (getHdl h) s >> return (h, ())) +setBinary b = lift (\h -> hSetBinaryMode (getHdl h) b >> return (h, ())) + {-# NOINLINE random #-} -- Random numbers have no interesting dependence on the world state, -- so it is not threaded. -random = A (\Blank -> unsafePerformIO $ liftM ((,) Blank) getStdGen) +random rng = A (\Blank -> unsafePerformIO $ getStdGen >>= \g -> let (x, g') = randomR rng g in setStdGen g' >> return (Blank, x)) -------------------------------------------------------- @@ -229,17 +270,17 @@ data Fix f = In (f (Fix f)) fixInj1 :: Pointer p s (Fix f) -> Pointer p s (f (Fix f)) -fixInj1 (Pointer fp p world) = Pointer fp (castPtr p) world +fixInj1 (Pointer fp p) = Pointer fp (castPtr p) fixInj2 :: Pointer p s (f (Fix f)) -> Pointer p s (Fix f) -fixInj2 (Pointer fp p world) = Pointer fp (castPtr p) world +fixInj2 (Pointer fp p) = Pointer fp (castPtr p) -data Pointer p s t = Pointer !(ForeignPtr Blank) !(Ptr t) (State# RealWorld) +data Pointer p s t = Pointer !(ForeignPtr Blank) !(Ptr t) instance Storable (Pointer p s t) where sizeOf _ = 8 alignment _ = 4 - poke p (Pointer fp p2 _) = do -- The RealWorld is lost... + poke p (Pointer fp p2) = do sp <- newStablePtr fp pokeByteOff p 0 sp pokeByteOff p 4 p2 @@ -248,7 +289,7 @@ fp <- deRefStablePtr sp freeStablePtr sp p2 <- peekByteOff p 4 - IO (\s -> (# s, Pointer fp p2 s #)) -- ...so produce one here. + return (Pointer fp p2) -- | Pointers can be linear, nonlinear, or focused. There are the following -- tradeoffs: @@ -268,11 +309,11 @@ data Placeholder = Placeholder -class Splitable s +class Splittable s -instance Splitable Nonlinear +instance Splittable Nonlinear -instance Splitable Focused +instance Splittable Focused {-# NOINLINE dummy #-} dummy :: ForeignPtr Blank @@ -282,70 +323,59 @@ contraction = A (\p -> (Pair p p, ())) class Weakening t where - weakening :: A () t Blank + weakening :: A () (Pair t St) St instance Weakening (Pointer p Focused t) where - weakening = A (\(Pointer _ _ _) -> (Blank, ())) + weakening = A (\(Pair (Pointer _ _) st) -> (st, ())) instance Weakening (Pointer p Nonlinear t) where - weakening = A (\(Pointer fp _ world) -> let IO f = touchForeignPtr fp in - case f world of (# _, () #) -> (Blank, ())) + weakening = drop1 . lift (\(Pointer fp _) -> touchForeignPtr fp >> return (Blank, ())) instance Weakening (Open p) where - weakening = A (\(Open _) -> (Blank, ())) + weakening = A (\(Pair (Open _) st) -> (st, ())) -- | Allocate a new linear block (containing junk), Use 'poke'' to initialize it. {-# NOINLINE new #-} -new :: (Storable t) => A () Blank (Pointer p Placeholder t) -new = A (\Blank -> unsafePerformIO (liftM (\p -> (Pointer dummy p realWorld#, ())) A.malloc)) +new :: (Storable t) => A () St (Pair (Pointer p Placeholder t) St) +new = lift (\Blank -> liftM (\p -> (Pointer dummy p, ())) A.malloc) . undrop1 --- By using a smuggled RealWorld, I sequence freeing of a pointer without --- requiring explicit world sequencing. -- | Use 'peek'' to take ownership of the contents of a block before freeing it. -free :: A () (Pointer p2 Placeholder t) Blank -free = A (\(Pointer _ p world) -> let IO f = A.free p in - case f world of (# _, () #) -> (Blank, ())) +free :: A () (Pair (Pointer p2 Placeholder t) St) St +free = drop1 . lift (\(Pointer _ p) -> A.free p >> return (Blank, ())) -- | Split a pointer to a pair, into a pair of pointers. -split :: forall t u p s. (Storable t, Storable u, Splitable s) => A () (Pointer p s (Pair t u)) (Pair (Pointer p s t) (Pointer p s u)) -split = A (\(Pointer fp p _) -> (Pair - (Pointer fp (frst p) realWorld#) - (Pointer fp (secnd p) realWorld#), ())) +split :: forall t u p s. (Storable t, Storable u, Splittable s) => A () (Pointer p s (Pair t u)) (Pair (Pointer p s t) (Pointer p s u)) +split = A (\(Pointer fp p) -> (Pair + (Pointer fp (frst p)) + (Pointer fp (secnd p)), ())) ptrSwap :: (Storable t) => Fn (Pair (Pointer p s t) t) (Pair (Pointer p s t) t) -ptrSwap = lift (\(Pair ptr@(Pointer _ p _) x) -> peek p >>= \y -> poke p x >> return (Pair ptr y, ())) >>> updateWorld1 +ptrSwap = lift (\(Pair ptr@(Pointer _ p) x) -> peek p >>= \y -> poke p x >> return (Pair ptr y, ())) -- | Focusing on a pointer. -- -- Temporarily turns a linear pointer into a focused pointer. I get the linear -- pointer back after all copies have been surrendered (with 'weakening'). -focus :: (forall p. A a (Pair (Pointer p Focused t) u) (Pair v St)) - -> A a (Pair (Pointer p s t) u) (Pair (Pair (Pointer p s t) v) St) -focus (A f) = A (\(Pair ptr@(Pointer fp p _) x) -> first (\(Pair x st) -> Pair (Pair ptr x) st) (f (Pair (Pointer fp p realWorld#) x))) >>== \x -> updateWorld1 >>== \_ -> rtn x +focus :: (forall p. A a (Pair (Pointer p Focused t) u) v) + -> A a (Pair (Pointer p s t) u) (Pair (Pointer p s t) v) +focus (A f) = A (\(Pair ptr@(Pointer fp p) x) -> first (Pair ptr) (f (Pair (Pointer fp p) x))) -- | Focusing on a handle. focusHdl :: (forall p. A a (Pair (Open p) t) u) -> A a (Pair Exclusive t) (Pair Exclusive u) focusHdl (A f) = A (\(Pair h@(Exclusive hdl) x) -> first (Pair h) (f (Pair (Open hdl) x))) -updateWorld :: A () (Pair (Pointer p s t) St) (Pair (Pointer p s t) St) -updateWorld = A (\(Pair (Pointer fp p _) st@(St world)) -> (Pair (Pointer fp p world) st, ())) - -manipulate = assoc2 . bimap' id swap . assoc1 - -updateWorld1 = manipulate . bimap' updateWorld id . manipulate - -- | Take the data out of a block, making it a placeholder. peek' :: (Storable t) => Fn (Pointer p Linear t) (Pair (Pointer p Placeholder t) t) -peek' = updateWorld1 . lift (\(Pointer fp p st) -> liftM (\x -> (Pair (Pointer fp p st) x, ())) (peek p)) +peek' = lift (\(Pointer fp p) -> liftM (\x -> (Pair (Pointer fp p) x, ())) (peek p)) -- | The reverse operation. poke' :: (Storable t) => Fn (Pair (Pointer p Placeholder t) t) (Pointer p Linear t) -poke' = updateWorld . lift (\(Pair (Pointer fp p world) x) -> poke p x >> return (Pointer fp p world, ())) +poke' = lift (\(Pair (Pointer fp p) x) -> poke p x >> return (Pointer fp p, ())) -- | A placeholder block can change its type. changeType :: forall t u p. (Storable t, Storable u) => A () (Pointer p Placeholder t) (Pointer p Placeholder u) changeType = if sizeOf (undefined :: u) <= sizeOf (undefined :: t) then - A (\(Pointer fp p world) -> (Pointer (castForeignPtr fp) (castPtr p) world, ())) + A (\(Pointer fp p) -> (Pointer (castForeignPtr fp) (castPtr p), ())) else error "Control.Linear.changeType: value won't fit" @@ -357,20 +387,46 @@ p <- A.malloc poke p x fp <- newForeignPtr_ p - return (Pointer (castForeignPtr fp) p realWorld#, ())) + return (Pointer (castForeignPtr fp) p, ())) peek1 :: (Storable t) => A t (Pair (Pointer Nonlinear s t) St) (Pair (Pointer Nonlinear s t) St) -peek1 = lift (\ptr@(Pointer _ p _) -> liftM (\x -> (ptr, x)) $ peek p) >>== \x -> updateWorld >>== \_ -> rtn x +peek1 = lift (\ptr@(Pointer _ p) -> liftM (\x -> (ptr, x)) $ peek p) poke1 :: (Storable t) => t -> Fn (Pointer p s t) (Pointer p s t) -poke1 x = lift (\ptr@(Pointer _ p _) -> poke p x >> return (ptr, ())) >>> updateWorld +poke1 x = lift (\ptr@(Pointer _ p) -> poke p x >> return (ptr, ())) +-- | Duplicate the world state. This is interpreted as creating a thread. +fork :: A () St (Pair St St) +fork = A (\st -> (Pair st st, ())) + +-- St --------new----X +-- \ +-- \ +-- \ +-- St ------------free----- St +-- +-- By exchanging a pointer, +-- | Sync together two world states. +{-# NOINLINE join' #-} +join' :: A () (Pair St St) St +join' = A (\(Pair _ st) -> (st, ())) . bimap' id free . assoc1 . bimap' (swap . (new :: A () St (Pair (Pointer p Placeholder Blank) St))) id + ------------------------------------------------------ --- Sample programs -helloWorld = run $ undrop1 +helloWorld = undrop1 >>> bimap' getStdout id >>> putS "Hello world!\n" + >>> weakening + +printStuff = undrop1 + >>> bimap' getStdout id + >>> iterate (putS "Stuff\n" >>>) id !! 10000 + >>> weakening + +concurrent = fork + >>> bimap' (open "C:\\users\\james\\videos\\Biggest number.wmv" ReadMode) printStuff + >>> bimap' (contents >>== \text -> last text `seq` close1 >>> undrop1 >>> bimap' getStdout id >>> putS (take 10000 text)) id >>> bimap' weakening id - >>> drop1 + >>> join'
Control/Monad/Distributive.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FunctionalDependencies, TypeOperators #-} +{-# LANGUAGE Safe, MultiParamTypeClasses, FlexibleContexts, FunctionalDependencies, TypeOperators #-} module Control.Monad.Distributive where @@ -75,4 +75,4 @@ ldist' m = putin $ ldist m -- | Right distributivity. -rdist' m = rdist (takeout m) >>= combine +rdist' m = rdist (takeout m) >>= lift . combine
Control/Monad/IOT.hs view
@@ -1,80 +1,114 @@-{-# LANGUAGE MagicHash, UnboxedTuples, Rank2Types, GADTs #-} +{-# LANGUAGE Trustworthy, Rank2Types, MagicHash, UnboxedTuples, BangPatterns #-} -module Control.Monad.IOT (IOT, run) where +module Control.Monad.IOT (IOT, run, module Control.Monad.Trans, module Control.Monad.Identity, module Control.Monad.Morph) where -import GHC.IO hiding (liftIO) +import GHC.IO (IO(IO)) import GHC.Prim import Control.Monad.Trans (MonadIO(..)) import Control.Monad.Identity import Control.Monad.Morph import Control.Monad import Control.Applicative +import Control.Concurrent.MVar +import Data.Typeable import Unsafe.Coerce -data Ret a = Ret (State# RealWorld) a - -data Sequence m where - None :: Sequence m - Seq :: (Monad m) => IO (Ret ()) -> Sequence (IOT m) - -{-# NOINLINE runSequence #-} -runSequence :: (Monad m) => Sequence m -> State# RealWorld -> m (Ret ()) -runSequence None s = return (Ret s ()) -runSequence (Seq io) _ = liftIO io +data State = State !(State# RealWorld) !(MVar ()) -- | An IO monad transformer. -- --- I can't run 'IOT'. Instead, I run the monad inside it. --- This is done using 'run', and 'hoist' from mmorph. +-- 'IOT' cannot be unwrapped in the usual way -- the monad inside it +-- has to be unwrapped. This is done using 'run', and 'hoist' from mmorph. -- --- The combination is only a monad if the parameter monad --- isn't nondeterministic. IOT Maybe and IOT State are --- monads, but IOT [] and IOT Cont are not. +-- Most of the safety of the IO monad is ensured statically. +-- However, to ensure that the same RealWorld token is not +-- used multiple times, a runtime check is necessary. Among +-- the alternatives that perform I/O, the first alternative +-- forced by a concatenation of 'hoist's will contain a result, +-- and subsequent alternatives will be errors. -- --- Should be integrated with STT. - -newtype IOT m t = IOT (Sequence m -> State# RealWorld -> m (Ret t)) +-- Therefore, a concatenation of 'hoists' out of a monad defines +-- at most one path of RealWorld token use. Here is an example using +-- the binary tree monad: +-- +-- >>> let io :: IOT Tree () = lift (Node (Leaf 1) (Leaf 2)) >>= liftIO . print +-- +-- >>> run $ hoist (\(Node (Leaf x) _) -> Identity x) io +-- 1 +-- +-- >>> run $ hoist (\(Node _ (Leaf x)) -> Identity x) io +-- 2 +-- +-- >>> run $ hoist (\(Node (Leaf _) (Leaf x)) -> Identity x) io +-- 1 +-- *** Exception: IOT: double RealWorld use +-- +newtype IOT m t = IOT (State -> m (State, t)) instance (Monad m) => Monad (IOT m) where - 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) + {-# INLINE return #-} + return x = IOT $ \s -> return (s, x) + {-# INLINE (>>=) #-} + IOT f >>= g = IOT $ \s -> f s >>= \(s, x) -> let IOT h = g x in h s instance (Monad m) => Applicative (IOT m) where + {-# INLINE pure #-} pure = return + {-# INLINE (<*>) #-} (<*>) = ap instance (Monad m) => Functor (IOT m) where + {-# INLINE fmap #-} fmap f m = m >>= return . f +err = error "IOT: double RealWorld use" + instance (Monad m) => MonadIO (IOT m) where - liftIO (IO f) = IOT (\_ s -> case f s of - (# s2, x #) -> return (Ret s2 x)) + {-# INLINE liftIO #-} + liftIO m = IOT $ \(State s mv) -> let + IO f = do + tryTakeMVar mv >>= maybe err return + liftM2 (,) m (newMVar ()); + (# s', (x, mv') #) = f s in + return (State s' mv', x) instance MonadTrans IOT where - 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, Ret s () #)) s in g i s >>= \(Ret _ pr) -> return pr) + {-# INLINE lift #-} + lift m = IOT $ \s -> liftM (\x -> (s, x)) m +{-# INLINE _hoist #-} _hoist :: (forall t. m t -> n t) -> IOT m t -> IOT n t -_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. +_hoist f (IOT g) = IOT (f . g) -instance MMonad IOT where - embed f = _squash . _hoist f +-- Squashes together two layers of IOTs. +{-# INLINE _squash #-} +_squash :: (Monad m) => IOT (IOT m) t -> IOT m t +_squash (IOT f) = do + mv <- liftIO $ newMVar () + (State _ m, x) <- IOT (\st@(State s _) -> let IOT g = f st in g (State s mv)) + liftIO (tryTakeMVar m) >>= maybe err return + return x instance MFunctor IOT where + {-# INLINE hoist #-} hoist = _hoist --- | Run an IOT. +instance MMonad IOT where + {-# INLINE embed #-} + embed f = _squash . _hoist f + +-- | Run an IOT yielding an IO computation. The 'Identity' monad is a trivial wrapper around IO. +{-# INLINE run #-} run :: IOT Identity t -> IO t -run (IOT f) = IO (\s -> case runIdentity (f None s) of - Ret s2 x -> (# s2, x #)) +run (IOT f) = do + mv <- newMVar () + (m, x) <- IO (\s -> case f (State s mv) of + Identity (State s' m, x) -> (# s', (m, x) #)) + tryTakeMVar m >>= maybe err return + return x + +{-# RULES +"void/newMVar" forall x. void (newMVar x) = return () +"newMVar/tryTakeMVar" forall x. newMVar x >>= tryTakeMVar = return (Just x) + #-} +
Control/Monad/Lifter.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverlappingInstances, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, UndecidableInstances, TypeOperators #-} +{-# LANGUAGE Unsafe, OverlappingInstances, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, UndecidableInstances, TypeOperators #-} module Control.Monad.Lifter where
Control/Monad/PlusMonad.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE RankNTypes, TypeOperators #-} +{-# LANGUAGE Safe, TypeOperators, RankNTypes, DeriveFunctor #-} -- | The Plus monad - a free combination of monads. This is very similar to coproducts, but not quite the same. -- -- Coproducts are due to Luth and Ghani, "Composing Monads Using Coproducts," http://www.informatik.uni-bremen.de/~cxl/papers/icfp02.pdf +-- + module Control.Monad.PlusMonad where import Control.Monad.Trans
MonadCompose.cabal view
@@ -1,5 +1,5 @@ name: MonadCompose -version: 0.8.2.0 +version: 0.8.3.0 synopsis: Methods for composing monads. description: Methods for composing monads. . @@ -21,4 +21,5 @@ library exposed-modules: Control.Monad.IOT, Control.Monad.Distributive, Control.Monad.PlusMonad, Control.Monad.Lifter, Control.Linear -- other-modules: - build-depends: base >=4 && <=5, ghc-prim ==0.3.*, mtl ==2.1.*, mmorph ==1.0.*, monad-products, transformers, MaybeT, random, data-default + build-depends: base >=4 && <=5, ghc-prim ==0.3.*, mtl ==2.1.*, mmorph ==1.0.*, monad-products, transformers, MaybeT, random, data-default, parallel ==3.2.*, comonad ==3.0.*, transformers-compat ==0.4.*, monad-loops >= 0.4.2.1 + ghc-options: -fno-cse