coroutine-object (empty) → 0.1
raw patch · 13 files changed
+714/−0 lines, 13 filesdep +basedep +bytestringdep +cerealsetup-changed
Dependencies added: base, bytestring, cereal, containers, lens, mtl, safecopy, transformers, transformers-free, uuid
Files
- LICENSE +30/−0
- Setup.lhs +10/−0
- coroutine-object.cabal +42/−0
- src/Control/Monad/Trans/Crtn.hs +154/−0
- src/Control/Monad/Trans/Crtn/Driver.hs +112/−0
- src/Control/Monad/Trans/Crtn/Event.hs +51/−0
- src/Control/Monad/Trans/Crtn/EventHandler.hs +41/−0
- src/Control/Monad/Trans/Crtn/IOActor.hs +41/−0
- src/Control/Monad/Trans/Crtn/Logger.hs +60/−0
- src/Control/Monad/Trans/Crtn/Logger/Simple.hs +24/−0
- src/Control/Monad/Trans/Crtn/Object.hs +65/−0
- src/Control/Monad/Trans/Crtn/Queue.hs +25/−0
- src/Control/Monad/Trans/Crtn/World.hs +59/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012 Ian-Woo Kim++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,10 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> import Distribution.PackageDescription+> --import System.Process+> main = defaultMain+> --main = defaultMainWithHooks testUserHooks+> --testUserHooks = simpleUserHooks { +> -- preConf = \_ _ -> runCommand "cd rootcode; make; cd .." >>return emptyHookedBuildInfo+> -- }
+ coroutine-object.cabal view
@@ -0,0 +1,42 @@+Name: coroutine-object+Version: 0.1+Synopsis: Object-oriented programming realization using coroutine +Description: Object-oriented programming realization using coroutine +License: BSD3+License-file: LICENSE+Author: Ian-Woo Kim+Maintainer: Ian-Woo Kim <ianwookim@gmail.com>+Build-Type: Simple+Cabal-Version: >= 1.8+data-files: +Library+ hs-source-dirs: src+ ghc-options: -Wall -O2 -threaded -funbox-strict-fields -fno-warn-unused-do-bind -fno-warn-orphans+ ghc-prof-options: -caf-all -auto-all+ Build-Depends: base >4 && <5, + mtl>2, + lens >= 2.4 && < 2.7,+ containers == 0.4.*,+ transformers == 0.3.*, + transformers-free == 1.0.*, + bytestring == 0.9.*,+ safecopy == 0.6.*,+ uuid == 1.2.*,+ cereal == 0.3.*+ Other-Modules: + exposed-modules:+ Control.Monad.Trans.Crtn+ Control.Monad.Trans.Crtn.Driver+ Control.Monad.Trans.Crtn.EventHandler + Control.Monad.Trans.Crtn.Event + Control.Monad.Trans.Crtn.IOActor + Control.Monad.Trans.Crtn.Logger+ Control.Monad.Trans.Crtn.Logger.Simple + Control.Monad.Trans.Crtn.Object + Control.Monad.Trans.Crtn.Queue + Control.Monad.Trans.Crtn.World+++++
+ src/Control/Monad/Trans/Crtn.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE StandaloneDeriving, TypeSynonymInstances, FlexibleInstances, + MultiParamTypeClasses, UndecidableInstances #-}++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn+-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- definition of coroutine+-- +-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn where ++import Control.Monad.Error+import Control.Monad.Reader +import Control.Monad.State +import Control.Monad.Trans.Free+++----------------------------+-- added instance of FreeT monad+----------------------------++instance (Monad m, MonadState st m, Functor f) => MonadState st (FreeT f m) where + get = lift get+ put = lift . put++---------------------------+-- general generator +---------------------------++-- | yield command functor+data Yld o x = Yld o x ++instance Functor (Yld o) where + fmap f (Yld o x) = Yld o (f x)++-- | Generator type is single-sided coroutine which only gives an output +-- without getting any request. +type GenT o = FreeT (Yld o)++-- | primitive coroutine action yielding an output+yield :: (Monad m) => o -> GenT o m () +yield o = wrap (Yld o (return ()))++---------------------------+-- general consumer+---------------------------++-- | await command functor for consumer coroutine+newtype Awt a x = Awt (a->x)++-- | +instance Functor (Awt g) where + fmap f (Awt g) = Awt (f.g)++-- | Consumer type is a single-sided coroutine which only gets an input +-- without producing output. +type CnsmT a = FreeT (Awt a) ++-- | primitive coroutine action awaiting an input+await :: (Monad m) => CnsmT a m a+await = wrap (Awt return)++----------------------------+-- general coroutine+----------------------------++-- | command functor of general bidirectional coroutine+data Rqst req ans x = Rqst req (ans -> x)++instance Functor (Rqst req ans) where+ fmap f (Rqst req g) = Rqst req (f.g)++-- | general symmetric bidirectional coroutine+type CrtnT req ans = FreeT (Rqst req ans)++-- | primitive request coroutine +request :: Monad m => req -> CrtnT req ans m ans+request r = wrap (Rqst r return)++-------------------------------+-- server/client model+------------------------------++-- | Server type +type SrvT req ans m = ReaderT req (CrtnT ans req m) ++-- | Coroutine type is regarded as a Client type +-- which can be paired with Server type with opposite request +-- and answer type. +type CliT req ans = CrtnT req ans ++-- | type for coroutine status after execution+data CrtnErr r = NoError + | ServerFinished r + | Other String ++-- | +deriving instance (Show r) => Show (CrtnErr r) ++-- | +instance Error (CrtnErr r) where + noMsg = NoError + strMsg str = Other str ++-----------------------------+-- communication combinator +-------------------------------++-- | connecting server and client in error monad+(<==|) :: Monad m => + SrvT req ans m r' -- ^ server coroutine+ -> CliT req ans m r -- ^ client coroutine+ -> ErrorT (CrtnErr r') m (SrvT req ans m r', r)+s <==| c = do + y <- lift (runFreeT c)+ case y of+ Pure r -> return (s,r)+ Free (Rqst rq af) -> do + x <- lift (runFreeT (runReaderT s rq))+ case x of + Pure r' -> throwError (ServerFinished r')+ Free (Rqst ans rf) -> (ReaderT rf) <==| (af ans)++{-+-- | synonym of connectE+(<==>) :: Monad m => SrvT req ans m r' -> CliT req ans m r+ -> ErrorT (CrtnErr r') m (SrvT req ans m r', r)+(<==>) = connectE+ -} +++----------------------+-- some utility +------------------------++-- | combine state and free monad with base state monad transformer +-- with a base monad m to free monad with the base monad m+-- Think this as fusing down the state monad +mapStateDown :: (Monad m, Functor f) => + s -> FreeT f (StateT s m) a -> FreeT f m a +mapStateDown st m =+ FreeT $ do x <- flip runStateT st $ runFreeT m + case x of + (Pure r,_) -> return (Pure r) + (Free f,st') -> + return . Free . fmap (mapStateDown st') $ f
+ src/Control/Monad/Trans/Crtn/Driver.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE GADTs, FlexibleInstances, ScopedTypeVariables #-}++----------------------------+-- | IO event driverzo+--+----------------------------++module Control.Monad.Trans.Crtn.Driver where ++import Control.Monad.Error+import Control.Monad.Reader+import Data.Foldable+-- +import Control.Monad.Trans.Crtn +import Control.Monad.Trans.Crtn.Event +import Control.Monad.Trans.Crtn.Logger +import Control.Monad.Trans.Crtn.Object+import Control.Monad.Trans.Crtn.World +-- ++-- | signature of IO event driver+data DrvOp e i o where + Dispatch :: DrvOp e e (Maybe (ActionOrder e)) ++-- | driver +type Driver e m = SObjT (DrvOp e) m ++-- | driver client +type DrvClient e m r = CObjT (DrvOp e) m r ++-- | +dispatch :: (Monad m) => e -> DrvClient e m (Maybe (ActionOrder e)) +dispatch ev = do Res Dispatch r <- request (Arg Dispatch ev) + return r+ +-- | basic driver +driver :: forall m e. (Monad m, MonadLog m, MonadIO m) => + LogServer (SObjBT (DrvOp e) m) ()+ -> SObjT (WorldOp e (SObjBT (DrvOp e) m)) (SObjBT (DrvOp e) m) () + -> Driver e m () +driver logger world = + ReaderT (driverW logger world) + where + driverW :: LogServer (SObjBT (DrvOp e) m) () + -> SObjT (WorldOp e (SObjBT (DrvOp e) m)) (SObjBT (DrvOp e) m) () + -> Arg (DrvOp e)+ -> SObjBT (DrvOp e) m () + driverW logobj worldobj (Arg Dispatch ev) = do + (logobj',worldobj') <- multiDispatchTillEnd (logobj,worldobj) [Right ev]+ req <- request (Res Dispatch Nothing) + driverW logobj' worldobj' req ++++-- | single event dispatch +singleDispatch :: (Monad m) => + Either (ActionOrder e) e+ -> ( LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ()+ , [EvOrAct e])+ -> SObjBT (DrvOp e) m + ( LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ()+ , [EvOrAct e])+singleDispatch (Right ev) (logobj,worldobj,evacc) = do+ Right (logobj',worldobj',events) <- + runErrorT $ do (worldobj1,_) <- worldobj <==| giveEvent ev+ (worldobj2,logobj1) <- worldobj1 <==| flushLog logobj+ (worldobj3,events) <- worldobj2 <==| flushQueue + return (logobj1,worldobj3,events)+ return (logobj',worldobj',evacc++events) +singleDispatch (Left act) (logobj,worldobj,evacc) = do + Arg Dispatch ev <- request (Res Dispatch (Just act))+ return (logobj,worldobj,evacc++[Right ev]) ++++-- | a single feedback step of multiple event dispatch+multiDispatch :: (Monad m) => + ( LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ())+ -> [EvOrAct e]+ -> SObjBT (DrvOp e) m + ( LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ()+ , [EvOrAct e] )+multiDispatch (logobj,worldobj) events = do + foldrM singleDispatch (logobj,worldobj,[]) events ++-- | full multiple event dispatch with feedback+multiDispatchTillEnd :: (Monad m) => + ( LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ()) + -> [EvOrAct e] + -> SObjBT (DrvOp e) m + (LogServer (SObjBT (DrvOp e) m) ()+ , World e (SObjBT (DrvOp e) m) ())+multiDispatchTillEnd (logobj,worldobj) events = + go (logobj,worldobj,events)+ where go (l,w,evs) = do + (l',w',evs') <- multiDispatch (l,w) evs + if (not.null) evs' + then go (l',w',evs')+ else return (l',w')+ ++-- | convenience routine for driver +fire :: (Monad m, MonadLog m) => e -> EStT (Driver e m ()) m + (Maybe (ActionOrder e)) +fire = query . dispatch ++
+ src/Control/Monad/Trans/Crtn/Event.hs view
@@ -0,0 +1,51 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn.Event+-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- coroutine that can be interpreted as an object+-- +-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn.Event where ++{-+import Data.ByteString +import Data.UUID+import Data.SafeCopy++class (Show e,Eq e, SafeCopy e) => Eventable e where + eventClassID :: e -> UUID + eventWrap :: e -> Event ++-- | event +data Event = Event (UUID,ByteString)+ deriving (Show,Eq)+++{-+data Event = Message String + | Open + | Close + | Render + | Sound String + | Start+ | Finished + | Init Int + | Finish Int + deriving (Show,Eq) -}+-}++-- | action order +data ActionOrder e = ActionOrder ((e -> IO ()) -> IO e)++++-- | event or action+type EvOrAct e = Either (ActionOrder e) e +
+ src/Control/Monad/Trans/Crtn/EventHandler.hs view
@@ -0,0 +1,41 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn.EventHandler+-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- Common event handling +-- +-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn.EventHandler where + +import Control.Concurrent.MVar +import Control.Monad.State +import Control.Monad.Error+-- from this package +-- import Control.Monad.Trans.Crtn+import Control.Monad.Trans.Crtn.Event +import Control.Monad.Trans.Crtn.Driver +import Control.Monad.Trans.Crtn.Logger ++-- | +eventHandler :: MVar (Maybe (Driver e IO ())) -> e -> IO ()+eventHandler evar ev = do + mnext <- takeMVar evar+ case mnext of + Nothing -> return () + Just drv -> do + (r,drv') <- eaction drv + putMVar evar (Just drv')+ case r of + Left err -> scribe (show err) + Right Nothing -> return () + Right (Just (ActionOrder act)) -> + act (eventHandler evar) >>= eventHandler evar + where eaction = runStateT (runErrorT $ fire ev) +
+ src/Control/Monad/Trans/Crtn/IOActor.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE GADTs, NoMonomorphismRestriction, ScopedTypeVariables #-}++------------------------------+-- | special actor for IO action +------------------------------++module Control.Monad.Trans.Crtn.IOActor where++import Control.Monad.Reader+-- +import Control.Monad.Trans.Crtn+-- import Control.Monad.Trans.Crtn.Event +import Control.Monad.Trans.Crtn.Object +++-- | first is +data IOOp e i o where+ DoIOAction :: IOOp e ((e -> IO ()) -> IO ()) (Either String ())++type IOActor e m r = SObjT (IOOp e) m r++-- | +doIOAction :: (Monad m) => ((e -> IO ()) -> IO ()) + -> CObjT (IOOp e) m (Either String ())+doIOAction act = do ans <- request (Arg DoIOAction act) + case ans of + Res DoIOAction r -> return r+ Ign -> return (Left "error in doing doIOAction")++-- | +ioactorgen :: (MonadIO m) => (e -> IO ()) -> SObjT (IOOp e) m ()+ioactorgen evhandler = ReaderT ioactorW + where ioactorW (Arg DoIOAction act) = do + liftIO (act evhandler) + req <- request (Res DoIOAction (Right ()))+ ioactorW req +++ ++
+ src/Control/Monad/Trans/Crtn/Logger.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE GADTs, FlexibleInstances, UndecidableInstances, OverlappingInstances #-}++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn.Logger +-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- describe logger+--+-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn.Logger where++import Control.Monad.Reader+--+import Control.Monad.Trans.Crtn +import Control.Monad.Trans.Crtn.Object++-------------------------+-- Logging monad +-------------------------++class (Monad m) => MonadLog m where + scribe :: String -> m () ++instance (MonadTrans t, MonadLog m, Monad (t m)) => MonadLog (t m) where + scribe = lift . scribe + +instance MonadLog IO where+ scribe = putStrLn +++data LogOp i o where + WriteLog :: LogOp String () +++type LogInput = Arg LogOp ++type LogServer m r = SObjT LogOp m r ++type LogClient m r = CObjT LogOp m r++-- | +writeLog :: (Monad m) => String -> LogClient m () +writeLog msg = do request (Arg WriteLog msg) + return () +++++++++
+ src/Control/Monad/Trans/Crtn/Logger/Simple.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE GADTs #-}++module Control.Monad.Trans.Crtn.Logger.Simple where++import Control.Monad.Reader +-- from this package+import Control.Monad.Trans.Crtn+import Control.Monad.Trans.Crtn.Logger +import Control.Monad.Trans.Crtn.Object+++-- | +simplelogger :: (MonadLog m) => LogServer m () +simplelogger = loggerW 0+ +-- |+loggerW :: (MonadLog m) => Int -> LogServer m () +loggerW num = ReaderT (f num)+ where f n req = + case req of + Arg WriteLog msg -> do lift (scribe ("log number "++show n++" : "++ msg))+ req' <- request (Res WriteLog ())+ f (n+1) req' +
+ src/Control/Monad/Trans/Crtn/Object.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE GADTs, RankNTypes #-}++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn.Object +-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- coroutine that can be interpreted as an object+-- +-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn.Object where ++-- from other packages+import Control.Monad.State +import Control.Monad.Error +-- from this package+import Control.Monad.Trans.Crtn++-- | input of method of an object with signature s +data Arg s = forall i o. Arg (s i o) i ++-- | output of methdo of an object with signature s +data Res s = forall i o. Res (s i o) o + | Ign++{-+-- |+type SObjT s = CrtnT (Arg s) (ResMethodInput s) ++-- | +type ClientT s = CoroutineT (MethodInput s) (MethodOutput s)+-}++-- | Server object+type SObjT s m = SrvT (Arg s) (Res s) m + +-- | Client object+type CObjT s = CliT (Arg s) (Res s) ++-- | Server object base coroutine+type SObjBT s m = CrtnT (Res s) (Arg s) m+++++-- | convenient error state monad for object +type EStT s m = ErrorT (CrtnErr ()) (StateT s m) ++-- | +query :: (Monad m) => CObjT s m r -> EStT (SObjT s m ()) m r +query cli = do + qserv <- lift get + (qserv',r) <- mapErrorT lift (qserv <==| cli )+ lift (put qserv')+ return r+ + ++
+ src/Control/Monad/Trans/Crtn/Queue.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE StandaloneDeriving #-}++module Control.Monad.Trans.Crtn.Queue where ++---------------------------+-- queue+---------------------------+++data Queue a = Queue { fqueue :: [a] + , bqueue :: [a] } ++deriving instance (Show a) => Show (Queue a)++emptyQueue :: Queue a +emptyQueue = Queue [] []++enqueue :: a -> Queue a -> Queue a +enqueue y (Queue xs ys) = Queue xs (y:ys) ++dequeue :: Queue a -> (Queue a, Maybe a) +dequeue (Queue (x:xs) ys) = (Queue xs ys, Just x)+dequeue q@(Queue [] []) = (q,Nothing)+dequeue (Queue [] ys) = dequeue (Queue (reverse ys) [])+
+ src/Control/Monad/Trans/Crtn/World.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE GADTs, NoMonomorphismRestriction, ScopedTypeVariables, KindSignatures #-}++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Crtn.World+-- Copyright : (c) 2012 Ian-Woo Kim+--+-- License : BSD3+-- Maintainer : Ian-Woo Kim <ianwookim@gmail.com>+-- Stability : experimental+-- Portability : GHC+--+-- describe world object+--+-----------------------------------------------------------------------------++module Control.Monad.Trans.Crtn.World where ++import Control.Monad.Error +-- +import Control.Monad.Trans.Crtn+import Control.Monad.Trans.Crtn.Event +import Control.Monad.Trans.Crtn.Logger +import Control.Monad.Trans.Crtn.Object+-- +import Prelude hiding ((.),id)+++-- | +data WorldOp e m i o where + GiveEvent :: WorldOp e m e ()+ FlushLog :: WorldOp e m (LogServer m ()) (LogServer m ())+ FlushQueue :: WorldOp e m () [Either (ActionOrder e) e]++-- | +type World e m r = SObjT (WorldOp e m) m r +++-- | +giveEvent :: (Monad m) => e -> CObjT (WorldOp e m) m () +giveEvent ev = request (Arg GiveEvent ev) >> return () +++-- | +flushLog :: (Monad m) => LogServer m () -> CObjT (WorldOp e m) m (LogServer m ()) +flushLog logobj = do req <- request (Arg FlushLog logobj) + case req of + Res FlushLog logobj' -> return logobj' + Ign -> return logobj + _ -> error "error in flushLog" -- allow partiality++-- | +flushQueue :: (Monad m) => CObjT (WorldOp e m) m [Either (ActionOrder e) e]+flushQueue = do req <- request (Arg FlushQueue ())+ case req of + Res FlushQueue lst -> return lst + Ign -> return [] + _ -> error "error in flushQueue" -- allow partiality+