packages feed

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 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