monad-task (empty) → 0.1.0
raw patch · 7 files changed
+295/−0 lines, 7 filesdep +basedep +mtldep +transformerssetup-changed
Dependencies added: base, mtl, transformers
Files
- Control/Monad/Task.hs +35/−0
- Control/Monad/Task/Class.hs +102/−0
- Control/Monad/Trans/Task.hs +93/−0
- LICENSE +20/−0
- README.md +6/−0
- Setup.hs +2/−0
- monad-task.cabal +37/−0
+ Control/Monad/Task.hs view
@@ -0,0 +1,35 @@+{- | ++Task monad transformer can help refactor event and callback heavy+programs into monads via co-routines. The idea is loosely+based on /Combining Events And Threads For Scalable Network Services/,+by Peng Li and Steve Zdancewic, in /PLDI/, 2007.+(<http://www.cis.upenn.edu/~stevez/papers/abstracts.html#LZ07>), but +with deterministic and co-oprative lightweight threads, +also known as co-routines, so that the base monad can be anything ranging +from IO to state monads, or your favorite monad transformer stack.++Besides, Task monad transformer also provides a simple mechanism to signal+and watch for events, which allows complex event processing logic to be+expressed as streamlined monadic co-routines.++Task monad transformer is essentially a ContT, or continuation transformer,+defined to extract the control flow of monadic programs with co-operative+multi-threading. After the CPS transformation, the program trace is then+executed with a simple round-robin scheduler.++-}++module Control.Monad.Task+ ( -- * MonadTask class+ MonadTask(..)+ -- * TaskT monad transformer+ , TaskT (..)+ -- * Functions+ , runTask+ , orElse+ ) where++import Control.Monad.Trans.Task+import Control.Monad.Task.Class+
+ Control/Monad/Task/Class.hs view
@@ -0,0 +1,102 @@+-- | The MonadTask class that defines the set of combinators to work with Task monad.+--+-- The operations for MonadTask are similar to those of co-routines, with the+-- addition of watching and signaling events. +--+-- We also define a set of auto lifting for common transformers. Note that we +-- purposely leave a case undefined where a state transformer goes on top of +-- a task monad, because such an operation is either unsound or has to roll+-- back the state (see @'Control.Monad.Trans.State.liftCallCC'@). So it's +-- recommended to keep TaskT on top of all StateT in a transformer stack.++{-# LANGUAGE UndecidableInstances, + FunctionalDependencies, + MultiParamTypeClasses, + FlexibleInstances #-}++module Control.Monad.Task.Class + ( -- * MonadTask class+ MonadTask(..)+ , orElse+ ) where++import Data.Monoid+import Control.Monad.Cont+import Control.Monad.Trans.Identity+import Control.Monad.Trans.List+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Reader+import qualified Control.Monad.Trans.Writer.Lazy as LazyWriter+import qualified Control.Monad.Trans.Writer.Strict as StrictWriter+import Control.Monad.Trans.Error++-- | @MonadTask@ specifies a task monad @m@ over an event type @e@.+class Monad m => MonadTask e m | m -> e where+ -- | @yield@ temporarily suspends current task to let others run. + yield :: m ()+ -- | @fork@ spawns a task and runs it immediate until it ends or + -- suspends before returning to current task.+ fork :: m a -> m ()+ -- | @watch@ suspends current task to wait for future events, and will+ -- resume execution when an event triggers its watching function. + watch :: (e -> Maybe a) -> m a+ -- | @signal@ broadcasts an event to all other tasks that are watching,+ -- and give those who wake up the priority to run.+ signal :: e -> m ()+ -- | @exit@ ends all tasks and return immediately.+ exit :: m ()++-- | @orElse@ is a helper function for combining two trigger functions +-- disjuctively, favoring the first one.+orElse :: (e -> Maybe a) -> (e -> Maybe b) -> e -> Maybe (Either a b)+orElse f g x = maybe (fmap Right (g x)) (Just . Left) (f x)++instance (Error e, Monad m, MonadTask a m) => MonadTask a (ErrorT e m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . runErrorT + watch = lift . watch+ signal = lift . signal++instance (Monad m, MonadTask a m) => MonadTask a (IdentityT m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . runIdentityT + watch = lift . watch+ signal = lift . signal++instance (Monad m, MonadTask a m) => MonadTask a (ListT m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . runListT + watch = lift . watch+ signal = lift . signal++instance (Monad m, MonadTask a m) => MonadTask a (MaybeT m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . runMaybeT + watch = lift . watch+ signal = lift . signal++instance (Monad m, MonadTask a m) => MonadTask a (ReaderT r m) where+ exit = lift exit+ yield = lift yield+ fork = ReaderT . (fork .) . runReaderT + watch = lift . watch+ signal = lift . signal++instance (Monoid w, Monad m, MonadTask a m) => MonadTask a (LazyWriter.WriterT w m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . LazyWriter.runWriterT + watch = lift . watch+ signal = lift . signal++instance (Monoid w, Monad m, MonadTask a m) => MonadTask a (StrictWriter.WriterT w m) where+ exit = lift exit+ yield = lift yield+ fork = lift . fork . StrictWriter.runWriterT + watch = lift . watch+ signal = lift . signal+
+ Control/Monad/Trans/Task.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE GADTs, + UndecidableInstances, + NoMonomorphismRestriction, + GeneralizedNewtypeDeriving, + MultiParamTypeClasses, + FlexibleInstances #-}++module Control.Monad.Trans.Task + ( -- * Task monad transformer+ TaskT (..)+ -- * Trace of a base monad+ , Trace (..)+ , runTrace+ -- * Task functions+ , taskToTrace+ , runTask+ ) where++import Control.Applicative+import Control.Monad.Reader.Class+import Control.Monad.State.Class+import Control.Monad.IO.Class+import Control.Monad.Trans+import Control.Monad.Trans.Cont+import Control.Monad.Task.Class+import Data.Either (partitionEithers)++-- | A @Trace m e@ represents the control flow of a mult-threaded task monad+-- defined over a base monad @m@ and event type @e@. +data Trace m e where+ EXIT :: Trace m e+ RET :: Trace m e+ YIELD :: m (Trace m e) -> Trace m e+ FORK :: m (Trace m e) -> m (Trace m e) -> Trace m e+ WATCH :: (e -> Maybe v) -> (v -> m (Trace m e)) -> Trace m e + SIGNAL :: e -> m (Trace m e) -> Trace m e++-- | @runTrace@ runs a trace to its completion in the base monad with a simple +-- round-robin scheduler.+runTrace :: Monad m => m (Trace m e) -> m ()+runTrace prog = loop [prog] []+ where+ loop [] _ = return ()+ loop (m:ms) ss = m >>= step+ where+ step EXIT = return ()+ step RET = loop ms ss+ step (YIELD t) = loop (ms ++ [t]) ss+ step (FORK t1 t2) = loop (t1:t2:ms) ss+ step (WATCH f g) = loop ms (WATCH f g : ss)+ step (SIGNAL e t) = loop (ms' ++ [t] ++ ms) ss'+ where (ms', ss') = partitionEithers evs+ evs = [ maybe (Right x) (Left . g) (f e) | x@(WATCH f g) <- ss ]++-- | Task monad transformer.+newtype TaskT e m a + = TaskT { runTaskT :: ContT (Trace m e) m a }+ deriving (Functor, Applicative, MonadIO)++-- | @tasktoTrace@ CPS-converts a task monad into a trace in its base monad. +taskToTrace :: Monad m => TaskT e m a -> m (Trace m e) +taskToTrace (TaskT (ContT f)) = f (\_ -> return RET)++-- | @runTask@ runs a task monad until to its completion, i.e., no more active+-- tasks to run, or until it exits.+--+-- * @'runTask' = 'runTrace' . 'taskToTrace'@+runTask :: Monad m => TaskT e m a -> m ()+runTask = runTrace . taskToTrace++instance Monad m => Monad (TaskT e m) where+ return = TaskT . return+ (>>=) m f = TaskT $ runTaskT m >>= runTaskT . f+ fail _ = TaskT $ ContT $ \_ -> return EXIT ++instance MonadTrans (TaskT e) where+ lift = TaskT . lift++instance MonadReader s m => MonadReader s (TaskT e m) where+ ask = TaskT ask+ local f = TaskT . local f . runTaskT ++instance MonadState s m => MonadState s (TaskT e m) where+ get = TaskT get+ put = TaskT . put++instance Monad m => MonadTask e (TaskT e m) where+ exit = TaskT $ ContT $ \_ -> return EXIT+ yield = TaskT $ ContT $ return . YIELD . ($())+ fork p = TaskT $ ContT $ return . FORK (taskToTrace p) . ($())+ watch f = TaskT $ ContT $ return . WATCH f + signal e = TaskT $ ContT $ return . SIGNAL e . ($())+
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2012 Paul H. Liu <paul@thev.net>++This software is provided 'as-is', without any express or implied+warranty. In no event will the authors be held liable for any damages+arising from the use of this software.++Permission is granted to anyone to use this software for any purpose,+including commercial applications, and to alter it and redistribute it+freely, subject to the following restrictions:++1. The origin of this software must not be misrepresented; you must not+ claim that you wrote the original software. If you use this software+ in a product, an acknowledgment in the product documentation would+ be appreciated but is not required.++2. Altered source versions must be plainly marked as such, and must not+ be misrepresented as being the original software.++3. This notice may not be removed or altered from any source+ distribution.
+ README.md view
@@ -0,0 +1,6 @@+monad-task+==========++Task monad transformer that turns event processing into co-routine programming.++See [Invert the Inversion of Control](http://www.thev.net/PaulLiu/invert-inversion.html) for a tutorial on writing a GLFW/OpenGL application using task monad.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ monad-task.cabal view
@@ -0,0 +1,37 @@+name: monad-task+version: 0.1.0+homepage: http://github.com/ninegua/monad-task+author: Paul Liu+maintainer: Paul Liu <paul@thev.net>+stability: experimental+category: Control+cabal-version: >= 1.6+build-type: Simple+synopsis: A monad transformer that turns event processing into co-routine programming.+description:+ Task monad transformer can help refactor event and callback + heavy programs into monads via co-routines. The idea is loosely+ based on /Combining Events And Threads For Scalable Network Services/,+ by Peng Li and Steve Zdancewic, in /PLDI/, 2007.+ (<http://www.cis.upenn.edu/~stevez/papers/abstracts.html#LZ07>), but + with deterministic and co-oprative lightweight threads, also known as + co-routines, so that the base monad can be anything ranging from IO + to state monads, or your favorite monad transformer stack.+license: BSD3+license-file: LICENSE++extra-source-files: + README.md++source-repository head+ type: git+ location: git://github.com/ninegua/monad-task.git++Library+ exposed-modules:+ Control.Monad.Task+ Control.Monad.Task.Class+ Control.Monad.Trans.Task+ build-depends: base < 6, mtl == 2.*, transformers < 0.4+ extensions:+ ghc-options: -Wall -fno-warn-unused-imports