packages feed

simple-actors 0.0.1 → 0.1.0

raw patch · 3 files changed

+506/−167 lines, 3 filesdep +chan-splitdep +contravariantdep +mtl

Dependencies added: chan-split, contravariant, mtl

Files

Control/Concurrent/Actors.lhs view
@@ -1,215 +1,399 @@-> {-# LANGUAGE GeneralizedNewtypeDeriving, ViewPatterns #-}+> {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}  This module exports a simple, idiomatic implementation of the Actor Model.  > module Control.Concurrent.Actors (->     -- * Actor computations->       Actor->     , Loop->     , NextActor(..)->     , ActorM()->     -- ** Building Actors->     , continue->     , continue_->     , done->     , aseq->     -- * Message passing and IO+>+>     {- | +>     Here we demonstrate a binary tree of actors that supports insert and query+>     operations:+>      +>     > import Control.Concurrent.Actors+>     > import Control.Applicative+>     > import Control.Concurrent.MVar+>     > +>     > -- the actor equivalent of a Nil leaf node:+>     > nil :: Behavior Operation+>     > nil = Receive $ do+>     >     (Query _ var) <- received +>     >     send var False -- signal Int is not present in tree+>     >     return nil     -- await next message+>     > +>     >    <|> do          -- else, Insert received+>     >     l <- spawn nil -- spawn child nodes+>     >     r <- spawn nil+>     >     branch l r . val <$> received  -- create branch from inserted val+>     >     +>     > -- a branch node with a value 'v' and two children+>     > branch :: Node -> Node -> Int -> Behavior Operation    +>     > branch l r v = loop where+>     >     loop = Receive $ do+>     >         m <- received +>     >         case compare (val m) v of+>     >              LT -> send l m+>     >              GT -> send r m+>     >              EQ -> case m of -- signal Int present in tree:+>     >                         (Query _ var) -> send var True+>     >                         _             -> return ()+>     >         return loop+>     > +>     > type Node = Mailbox Operation+>     > +>     > -- operations supported by the network:+>     > data Operation = Insert { val :: Int }+>     >                | Query { val :: Int+>     >                        , sigVar :: MVar Bool }+>     > +>     > insert :: Node -> Int -> IO ()+>     > insert t = send t . Insert+>     > +>     > -- MVar is in the 'SplitChan' class so actors can 'send' to it:+>     > query :: Node -> Int -> IO Bool+>     > query t a = do+>     >     v <- newEmptyMVar+>     >     send t (Query a v)+>     >     takeMVar v+>     +>     You can use the tree defined above in GHCi:+>     +>     >>> :l TreeExample.hs +>     Ok+>     >>> t <- spawn nil+>     >>> query t 7+>     False+>     >>> insert t 7+>     >>> query t 7+>     True+>+>     -}+>+>     -- * Actor Behaviors+>       Action()+>     , Behavior(..)+>     -- ** Composing Behaviors+>     , (<.|>)+>+>     -- * Available actions+>     -- ** Message passing+>     , Mailbox() >     , send->     -- ** Actor system output:+>     , received+>     , guardReceived+>     -- ** Spawning actors+>     {- | +>     The 'spawn' function will be sufficient for forking actors in most cases,+>     but launching mutually-communicating actors presents a problem.+>     .+>     In cases where a 'Behavior' needs access to its own 'Mailbox' or that of +>     an actor that must be forked later, the 'MonadFix' instance should be+>     used. GHC\'s \"Recursive Do\" make this especially easy:+>     .+>     > {-# LANGUAGE DoRec #-}+>     > beh = Receive $ do+>     >     i <- received+>     >     -- similar to the scoping in a "let" block:+>     >     rec b1 <- spawn (senderTo b2)+>     >         b2 <- spawn (senderTo b1)+>     >         b3 <- spawn (senderTo b3)+>     >     -- send initial messages to actors spawned above:+>     >     send b3 i+>     >     send "first" b2+>     >     yield+>     -}+>     , spawn+>     , spawn_+>     , spawnReading+>     -- ** Building an actor computation+>     {- | +>     An actor computation can be halted immediately by calling 'yield',+>     a synonym for 'mzero'. When an 'Action' calling @yield@ is composed with+>     another using @<|>@ the second takes over processing the /same/ input+>     which the former @yield@-ed on.+>+>     Here is an example of a computation using 'guard' which returns @mzero@ if+>     the test is false:+>+>     > foo c n = Receive $ +>     >       do i <- received+>     >          guard (n<10)+>     >          send c i+>     >          return (foo c $ n+1)+>     >+>     >   <|> do i <- received -- same as the 'i' above+>     >          send c $ "TENTH INPUT: "++i+>     >          return (foo c 0)+>+>     The @Monoid@ instance for 'Behavior' works on the same principle.+>     -}+>     , yield >     , receive->     , receiveList->     -- ** Mailbox->     , Mailbox()->     , newMailbox->     -- * Running Actors->     , Action()->     , forkActor->     , forkActorUsing->     , forkLoop->     , runActorUsing->     , runLoop+>+>     -- * Utility functions+>     , runBehavior_+>     , runBehavior +>+>     -- * Useful predefined @Behavior@s+>     , printB+>     , putStrB+>     , signalB+>     , constB+> >     ) where > > import Control.Monad+> import Control.Monad.Reader(ask)+> import qualified Data.Foldable as F > import Control.Monad.IO.Class-> import Control.Monad.Trans.Maybe-> import Control.Concurrent-> import Control.Applicative+> import Control.Concurrent(forkIO)+> import Data.Monoid+>+> -- from the contravariant package +> import Data.Functor.Contravariant+> -- from the chan-split package+> import Control.Concurrent.Chan.Split+>+> -- internal:+> import Control.Concurrent.Actors.Behavior -TODO?:-    - Function for combining mailboxes (make a monoid? Only allow doing this in IO?) -    Alternately: what if we delegated a single 'mainActor' that is in IO, and which-    any Actor can send a message to? This would be an IO event loop and we would run-    this IO loop from 'main' which would block until... so tired. -    RE: Mailbox Class:-        - Having such a class is a decent idea anyway since we may want to have-          a synchronous Mailbox type, in which case we should +------ CPP MACROS ------ -Here we define the Actor environment, similar to IO, in which we can launch new-Actors and send messages to Actors in scope. The implementation is hidden from-the user to enforce these restrictions.+These macros are only provided by cabal unfortunately.... makes it difficult to+work with GHCi: -> -- | The Actor encironment in which Actors can be spawned and sent messages-> newtype ActorM a = ActorM { actorM :: MaybeT IO a }->                  deriving (Monad, Functor, Applicative, ->                            Alternative, MonadPlus, MonadIO)->-> runActorM = runMaybeT . actorM+#if !MIN_VERSION_base(4,3,0)+> void :: (Monad m)=> m a -> m ()+> void = (>> return ())+#endif -First we define an Actor: a function that takes an input, maybe returning a new-actor:+------------------------ -    TODO: Consider making Actor the newtype and eliminating NextActor-      newtype Actor i = Actor { actor :: i -> ActorM (Actor i) }-      continue :: (i -> ActorM (Actor i)) -> ActorM (Actor i)+TODO+----- + 0.2.0:+    - performance testing:+        - take a look at threadscope for random tree test+        - get complete code coverage into simple test module+    - interesting: http://en.wikipedia.org/wiki/Huang%27s_algorithm+    - better method for waiting for threads to complete. should probbly use+       actor message passing+    - look into whether we should use Text lib instead of strings?+      OverloadedStrings?+        -import Data.String, make polymorphic over IsString+        -test if this lets us use it in importing module w/ OverloadedStrings+        extension+    - structured declarative and unit tests+    - Performance testing:+        - test performance vs. straight Chans, etc.+        - test out overhead of our various locks, especially difference if we+          scrap the snederLockMutex+    - some sort of exception handling technique via Actors+        (look at enumerator package)+    - investigate ways of positively influencing thread scheduling based on+       actor work agenda +    - strict send' function+    -Behavior -> enumeratee package translator (and vice versa)+        (maybe letting us use useful enumerators)+    - export some more useful Actors and global thingies+        - 'loop' which keeps consuming (is this provided by a class?)+        - function returning an actor to "load balance" inputs over multiple+          actors+        - an actor that sends a random stream?+        - a pre-declared Mailbox for IO?+    - provide an "adapter" for amazon SQS, allowing truly distributed message+      passing -> type Actor i = i -> ActorM (NextActor i)-> newtype NextActor i = NextActor { nextActor :: Actor i }  -Now some functions for building Actor computations: -> -- | Continue with a new Actor computation step-> continue :: Actor i -> ActorM (NextActor i)-> continue = return . NextActor -    IMPLEMENTATION NOTE: -        when an actor terminates, its mailbox persists and we                        -        currently provide no functions to query an actor's status.                    -        Signaling an actor's termination should be done with                          -        message passing.+CHAN TYPES+========== -> -- | Actor terminating:-> done :: ActorM (NextActor i)-> done = mzero+> -- | One can 'send' a messages to a @Mailbox@ where it will be processed+> -- according to an actor\'s defined 'Behavior'+> newtype Mailbox a = Mailbox { inChan :: InChan a }+>       deriving (Contravariant)+> -    IMPLEMENTATION NOTE: -        We might find that we can use the monoid abstraction, or -        that we should make Actor a newtype for other reasons. For-        now we have this for composing+We don't need to expose this thanks to the miracle of MonadFix and recursive do,+but this can be generated via the NewSplitChan class below if the user imports+the library: -> -- | compose two actors. The second will take over when the first exits-> aseq :: Actor i -> Actor i -> Actor i-> aseq f g i = NextActor <$> (nextf <|> return g)->     where nextf = (`aseq` g) . nextActor <$> f i+> newtype Messages a = Messages { outChan :: OutChan a }+>       deriving (Functor) +>+> -- Not sure how to derive this or if possible:+> instance SplitChan Mailbox Messages where+>     readChan = readChan . outChan+>     writeChan = writeChan . inChan+>     writeList2Chan = writeList2Chan . inChan+>+> instance NewSplitChan Mailbox Messages where+>     newSplitChan = fmap (\(i,o)-> (Mailbox i, Messages o)) newSplitChan+>  -A Loop is just an Actor that ignores its input. We provide some useful-functions for building and running such computations: -> -- | An Actor that discards its input, i.e. a simple loop.-> type Loop = ActorM (NextActor ())->-> -- | Continue with a Loop computation-> continue_ :: Loop -> ActorM (NextActor i)-> continue_ = fmap (NextActor . fixConst . nextActor)->     where fixConst c = const $ continue_ $ c () +ACTIONS+======= +Functionality is based on our underlying type classes, but users shouldn't need+to import a bunch of libraries to get basic Behavior building functionality. -Here we define the "mailbox" that an Actor collects messages from, and other-actors send messages to. It is simply a Chan with hidden implementation.+> infixl 3 <.|> -    IMPLEMENTATION NOTE: -        we make no attempt to ensure that only one actor is reading                    -        from a given Chan. This means two Actors can share the work                    -        reading from the same mailbox.-        -        If we want to change this in the future, Mailbox will contain-        a type :: TVar ThreadID+> -- | Sequence two @Behavior@s. After the first 'yield's the second takes over,+> -- discarding the message the former was processing. See also the 'Monoid'+> -- instance for @Behavior@.+> -- +> -- > b <.|> b' = b `mappend` constB b'+> (<.|>) :: Behavior i -> Behavior i -> Behavior i+> b <.|> b' = b `mappend` constB b' -        To implement synchronous chans (or singly-buffered chans), -        we can use a SyncMailbox type containing an MVar and-        possibly another Var for ensuring syncronicity. An MVar-        writer will never block indefinitely. Use a class for writing-        and reading these mailbox types.+The 'yield' function is so named because it is "relinquishing control", i.e. I+think the name reminds of the functionality of <|> and mappend (the last input+is passed along) and also has the meaning "quit". -     -> -- | the buffered message passing medium used between actors-> newtype Mailbox i = Mailbox { mailbox :: Chan i }+Its similarity (or not) to the 'enumerator' function of the same same may be a+source of confusion (or the opposite)... I'm not sure.++> -- | Immediately give up processing an input, perhaps relinquishing the input+> -- to an 'Alternative' computation or exiting the actor.+> -- +> -- > yield = mzero+> yield :: Action i a+> yield = mzero >+> -- | Useful to make defining a continuing Behavior more readable as a+> -- \"receive block\", e.g.+> --+> -- > pairUp out = Receive $ do+> -- >     a <- received+> -- >     receive $ do+> -- >         b <- received+> -- >         send out (b,a)+> -- >         return (pairUp out)+> --+> -- Defined: @receive = return . Receive@+> receive :: Action i (Behavior i) -> Action i (Behavior i)+> receive = return . Receive -    IMPLEMENTATION NOTE: -        We allow sending of messages to Actors in IO, treating the -        main thread as something of an Actor with special privileges;-        It can launch actors and message them, but also read as it -        pleases from Mailboxes+> -- | Return the message received to start this 'Action' block. /N.B/ the value+> -- returned here does not change between calls in the same 'Action'.+> --+> -- > received = ask+> received :: Action i i+> received = ask +> -- | Return 'received' message matching predicate, otherwise 'yield'.+> --+> -- > guardReceived p = ask >>= \i-> guard (p i) >> return i+> guardReceived :: (i -> Bool) -> Action i i+> guardReceived p = ask >>= \i-> guard (p i) >> return i -> -- | Send a message to an Actor. Actors can only be passed messages from other-> -- actors.-> send :: (Action m)=> Mailbox a -> a -> m ()-> send b = liftIOtoA . writeChan (mailbox b)+> -- | Send a message asynchronously. This can be used to send messages to other+> -- Actors via a 'Mailbox', or used as a means of output from the Actor system+> -- to IO since the function is polymorphic.+> -- .+> -- > send b = liftIO . writeChan b+> send :: (MonadIO m, SplitChan c x)=> c a -> a -> m ()+> send b = liftIO . writeChan b -> -- | Read a message from a mailbox in the IO monad. This can be used as the-> -- mechanism for output from an Actor system. Blocks if the actor is empty-> receive :: Mailbox o -> IO o-> receive = readChan . mailbox -> -- | Return a lazy list of mailbox contents-> receiveList :: Mailbox o -> IO [o]-> receiveList = getChanContents . mailbox -> -- | create a new mailbox that Actors can be launched to read from or-> -- send messages to in order to communicate with other actors-> newMailbox :: (Action m)=> m (Mailbox a)-> newMailbox = liftIOtoA newChan >>= return . Mailbox+FORKING AND RUNNING ACTORS:+===========================  -The Action class represents environments in which we can operate on actors. That-is we would like to be able to send a message in IO+> -- | Like 'spawn' but allows one to specify explicitly the channel from which+> -- an actor should take its input. Useful for extending the library to work+> -- over other channels.+> spawnReading :: (MonadIO m, SplitChan x c)=> c i -> Behavior i -> m ()+> spawnReading str = liftIO . void . forkIO . actorRunner +>     where actorRunner b =+>               readChan str >>= runBehaviorStep b >>= F.mapM_ actorRunner -> -- | monads in the Action class can participate in message passing and other-> -- Actor operations-> class Monad m => Action m where->     liftIOtoA :: IO a -> m a->->     forkA :: IO () -> m ()->     forkA io = liftIOtoA $ forkIO io >> return ()->-> instance Action IO where->     liftIOtoA = id++RUNNING ACTORS+--------------++These work in IO, returning () when the actor finishes with done/mzero:++> -- | Run a @Behavior ()@ in the main thread, returning when the computation+> -- exits.+> runBehavior_ :: Behavior () -> IO ()+> runBehavior_ b = runBehavior b [(),()..] >-> instance Action ActorM where->     liftIOtoA = ActorM . liftIO+> -- | run a 'Behavior' in the IO monad, taking its \"messages\" from the list.+> -- Useful for debugging @Behaviors@.+> runBehavior :: Behavior a -> [a] -> IO ()+> runBehavior b (a:as) = runBehaviorStep b a >>= F.mapM_ (`runBehavior` as)+> runBehavior _ _      = return ()   +FORKING ACTORS+-------------- -> -- | fork an actor, returning its mailbox-> forkActor :: (Action m)=> Actor i -> m (Mailbox i)-> forkActor a = do->     b <- newMailbox->     forkActorUsing b a->     return b->     -> -- | fork an actor that reads from the supplied Mailbox-> forkActorUsing :: (Action m)=> Mailbox i -> Actor i -> m ()-> forkActorUsing b = forkA . actorHandler b->-> -- | fork a looping computation which starts immediately-> forkLoop :: (Action m)=> Loop -> m ()-> forkLoop = forkA . runLoop  +> -- | Fork an actor performing the specified 'Behavior'. /N.B./ an actor +> -- begins execution of its 'headBehavior' only after a mesage has been +> -- received. See also 'spawn_'.+> spawn :: (MonadIO m)=> Behavior i -> m (Mailbox i)+> spawn b = do+>     (m,s) <- liftIO newSplitChan+>     spawnReading s b+>     return m >-> -- | run a Loop actor in the main thread, returning when the computation exits-> runLoop :: Loop -> IO ()-> runLoop l = runActorM l >>= ->              maybe (return ()) (runLoop . ($ ()) .  nextActor)+> -- | Fork a looping computation which starts immediately. Equivalent to+> -- launching a @Behavior ()@ and another 'Behavior' that sends an infinite stream of+> -- ()s to the former\'s 'Mailbox'.+> spawn_ :: (MonadIO m)=> Behavior () -> m ()+> spawn_ = liftIO . void . forkIO . runBehavior_   ->-> -- | run an Actor in the main thread, returning when the Actor exits-> runActorUsing :: Mailbox i -> Actor i -> IO ()-> runActorUsing = actorHandler  -Internal function that feeds the actor computation its values. This may be-extended to support additional functionality in the future.+USEFUL GENERAL BEHAVIORS+======================== -> actorHandler :: Mailbox i -> Actor i -> IO ()-> actorHandler (mailbox->c) = loop->     where loop a = readChan c >>= ->                     runActorM . a >>= ->                      maybe (return ()) (loop . nextActor)+> -- | Prints all messages to STDOUT in the order they are received,+> -- 'yield'-ing /immediately/ after @n@ inputs are printed.+> printB :: (Show s, Num n)=> n -> Behavior s+> printB = contramap (unlines . return . show) . putStrB +We want to yield right after printing the last input to print. This lets us+compose with signalB for instance:++    write5ThenExit = putStrB 5 `mappend` signalB c++and the above will signal as soon as it has printed the last message. If we try+to define this in a more traditional recursive way the signal above would only+happen as soon as the sixth message was received.++For now we allow negative++> -- | Like 'printB' but using @putStr@.+> putStrB :: (Num n)=> n -> Behavior String+> putStrB 0 = mempty --special case when called directly w/ 0+> putStrB n = Receive $ do+>     s <- received+>     liftIO $ putStr s+>     guard (n /= 1)+>     return $ putStrB (n-1)++> -- | Sends a @()@ to the passed chan. This is useful with 'mappend' for+> -- signalling the end of some other 'Behavior'.+> --+> -- > signalB c = Receive (send c () >> yield)+> signalB :: (SplitChan c x)=> c () -> Behavior i+> signalB c = Receive (send c () >> yield)++> -- | A @Behavior@ that discard its first input, returning the passed Behavior+> -- for processing subsequent inputs. Useful with 'Alternative' or 'Monoid'+> -- compositions when one wants to ignore the leftover input.+> --+> -- > constB = Receive . return+> constB :: Behavior i -> Behavior i+> constB = Receive . return
+ Control/Concurrent/Actors/Behavior.lhs view
@@ -0,0 +1,139 @@+> {-# 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:++> -- | An actor is created by 'spawn'ing a @Behavior@. Behaviors consist of+> -- a composed 'Action' that is executed when a message is 'received' and+> -- returns the @Behavior@ for processing the next input.+> 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 'abort'+> -- 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:+++> -- | In the Actor Model, at each step an actor...+> --+> --     - processes a single 'received' message+> --     +> --     - may 'spawn' new actors+> --     +> --     - may 'send' messages to other actors+> --     +> --     - 'return's the 'Behavior' for processing the /next/ message+> --+> -- These actions take place within the @Action i@ monad, where @i@ is the type+> -- of the input message the actor receives.+> --+> -- /N.B.:/ the MonadIO instance here is an abstraction leak. An example of a+> -- good use of 'liftIO' might be to give an @Action@ access to a source of+> -- randomness.+> 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
simple-actors.cabal view
@@ -7,13 +7,19 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version:             0.0.1+Version:             0.1.0  -- A short (one-line) description of the package.-Synopsis:            A simple implementation of the actor model of concurrency+Synopsis:            A library for more structured concurrent programming, based+                     on the Actor Model+Homepage:            http://coder.bsimmons.name/blog/2011/05/simple-actors-a-simple-actor-model-concurrency-library/  -- A longer description of the package-Description:         Simple concurrency primitives based on the Actor Model.+Description:         simple-actors is an EDSL-style library for writing+                     more structured concurrent programs, based on the Actor +                     Model. Computations are structured as "Behaviors" which take a+                     single input value, perform some 'Action's, and return the+                     Behavior to process the next input message it receives.  -- The license under which the package is released. License:             BSD3@@ -43,19 +49,29 @@ -- Extra-source-files:    -- Constraint on the version of Cabal needed to build this package.-Cabal-version:       >=1.2+Cabal-version:       >=1.6 +source-repository head   +    type:     git+    location: https://github.com/jberryman/simple-actors.git  Library   -- Modules exported by the library.   Exposed-modules:     Control.Concurrent.Actors      -- Packages needed in order to build this package.-  Build-depends:      transformers-                    , base >= 4 && < 5    +  Build-depends:       base >= 4 && < 5    +                     , chan-split+                     , mtl >= 2+                     , transformers+                     , contravariant+                       +  ghc-options:        -Wall+   -- Modules not exported by this package.-  -- Other-modules:       +  Other-modules:     Control.Concurrent.Actors.Behavior+                           -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.   -- Build-tools: