simple-actors-0.2.0: Control/Concurrent/Actors/Behavior.lhs
> {-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}
> -- Since we don't get a MonadFix instance for MaybeT from transformers:
> {-# OPTIONS_GHC -fno-warn-orphans #-}
> module Control.Concurrent.Actors.Behavior
> where
>
Our main data types for actors and behaviors are defined in this private module.
We may expose this if it is useful to people:
These imports are mostly for all the instances for Action that we define:
> -- using 'mtl':
> import Control.Monad.Reader(ReaderT(..))
> import Control.Monad.Reader.Class
> import Control.Monad.Trans.Maybe
> import Control.Monad.IO.Class
> import Control.Monad.Fix
> import Control.Applicative
> import Control.Monad
>
> import Control.Arrow
> import qualified Control.Category as C
> import Data.Monoid
>
> import Data.Functor.Contravariant
A Behavior wraps an Action i a, into a list-like sequence of actions to perform
over inputs:
> newtype Behavior i = Receive { headAction :: Action i (Behavior i) }
>
> instance Contravariant Behavior where
> contramap f (Receive a) = Receive $ f ^>> (contramap f <$> a)
> --contramap f = Receive . withReaderT f . fmap (contramap f) . headAction
>
This is essentially a marriage of the Monoid [] instance with Action's
Alternative instance, and I am mostly convinced it is right and has utility:
> -- | @b1 `mplus` b2@ has the 'headAction' of @b2@ begin where the 'yield'
> -- occured in @b1@, i.e. @b2@\'s first input will be the final input handed to
> -- @b1@.
> instance Monoid (Behavior i) where
> mempty = Receive mzero
> mappend (Receive a1) b2@(Receive a2) = Receive $
> -- is this the best way of defining this?:
> (flip mappend b2 <$> a1) <|> a2
Defining Action as a Reader / Maybe stack lets us have a nice EDSL syntax for
constructing behaviors, and I am able to derive a bunch of the instances that
will be useful:
> newtype Action i a = Action { readerT :: ReaderT i (MaybeT IO) a }
> deriving (Monad, MonadIO, MonadPlus, MonadReader i,
> Functor, Applicative, Alternative, MonadFix)
>
------ CPP MACROS ------
This should end up in the next version of 'transformers':
http://www.haskell.org/pipermail/libraries/2011-April/016201.html
#if !MIN_VERSION_transformers(0,3,0)
> instance (MonadFix m) => MonadFix (MaybeT m) where
> mfix f = MaybeT $ mfix (runMaybeT . f . unJust)
> where unJust = maybe (error "mfix MaybeT: Nothing") id
#endif
------------------------
Some helpers for wrapping / unwrapping:
> -- pack and unpack:
> runAction :: Action r a -> r -> MaybeT IO a
> runAction = runReaderT . readerT
>
> action :: (i -> MaybeT IO a) -> Action i a
> action = Action . ReaderT
>
> runBehaviorStep :: Behavior i -> i -> IO (Maybe (Behavior i))
> runBehaviorStep = fmap runMaybeT . runAction . headAction
Kleisli is ReaderT, so these are basically cribbed from its instances. It's a
shame ReaderT's type arguments are the way they are or we could have derived
this:
> instance C.Category Action where
> id = ask
> f . g = action $ \i-> runAction g i >>= runAction f
>
> instance Arrow Action where
> arr = action . fmap return
> first f = action $ \ ~(b,d)-> runAction f b >>= \c -> return (c,d)
>
> -- additional Arrow sub-classes:
> instance ArrowApply Action where
> app = action (uncurry runAction)
>
> instance ArrowChoice Action where
> left f = f +++ C.id
>
> -- and following that MaybeT IO is a MonadPlus...
> instance ArrowPlus Action where
> f <+> g = action $ \i-> runAction f i `mplus` runAction g i
>
> instance ArrowZero Action where
> zeroArrow = action $ const mzero
>
> -- inspired by MonadFix instance.
> instance ArrowLoop Action where
> loop af = action (liftM fst . mfix . f')
> where f' x y = f (x, snd y)
> f = runAction af