packages feed

simple-actors 0.3.0 → 0.4.0

raw patch · 2 files changed

+308/−100 lines, 2 filesdep ~contravariantdep ~mtl

Dependency ranges changed: contravariant, mtl

Files

Control/Concurrent/Actors.lhs view
@@ -1,4 +1,4 @@-> {-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}+> {-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeFamilies, TypeOperators #-}  This module exports a simple, idiomatic implementation of the Actor Model. @@ -42,16 +42,16 @@ >     > -- operations supported by the network: >     > data Operation = Insert { val :: Int } >     >                | Query { val :: Int->     >                        , sigVar :: MVar Bool }+>     >                        , sigVar :: Mailbox 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+>     >     -- turn an MVar into a Mailbox actors can send to with 'out' >     >     v <- newEmptyMVar->     >     send t (Query a v)+>     >     send t (Query a $ out v) >     >     takeMVar v >      >     You can use the tree defined above in GHCi:@@ -76,17 +76,23 @@ >     -- * Available actions >     -- ** Message passing >     , Mailbox()+>     , out >     , send , send' , (<->) >     , received >     , guardReceived >     -- ** Spawning actors+>     , Sources(), Joined+>   --, (:-:)(..)+>     , spawn+>     -- *** Mailboxes and scoping >     {- | ->     The 'spawn' function will be sufficient for forking actors in most cases,->     but launching mutually-communicating actors presents a problem.+>     Straightforward use of 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:+>     used. GHC\'s \"Recursive Do\" notation make this especially easy: >       >     > {-# LANGUAGE DoRec #-} >     > beh = Receive $ do@@ -97,14 +103,11 @@ >     >         b3 <- spawn (senderTo b3) >     >     -- send initial messages to actors spawned above: >     >     send b3 i->     >     send "first" b2+>     >     send b2 "first" >     >     yield > >     -} >->     , spawn->     , spawn_->     , spawnReading >     -- ** Building an actor computation >     {- |  >     An actor computation can be halted immediately by calling 'yield',@@ -136,10 +139,10 @@ >     product types.  >     -} >     , coproductMb->     , productMb+>     , contraProduct >     , zipMb->     , faninMb->     , fanoutMb+>     , contraFanin+>     , contraFanout > >     -- * Utility functions >     {- | @@ -157,6 +160,7 @@ >     ) where > > import Control.Monad+> import Control.Applicative > import Control.Monad.Reader(ask) > import qualified Data.Foldable as F > import Control.Monad.IO.Class@@ -168,7 +172,6 @@ > import Data.Functor.Contravariant > -- from the chan-split package > import Control.Concurrent.Chan.Split-> > -- internal: > import Control.Concurrent.Actors.Behavior @@ -178,28 +181,33 @@ TODO ----- -0.4-    - allow destructuring using UndecidableInstances (see mockup) on spawn, allowing for new, awesome synchronization semantics!-    - make that also work with Behaviors of arbitrary input types using new GHC generics!---Later:+0.4.1     - performance tuning / benchmarking:-        + look at interface file: ghc -ddump-hi Control/Concurrent/Actors.hs -O -c-        + remove current PRAGMA-        - close browser and everything, do a fake quick benchmark to get clock info-        - be more controlled about the source lists (do once before defaultMain), use 'evaluate'-        - run with +RTS -s and make sure everything is 0-        - see if case-based nil is better-        - get accurate baseline comparison between actors and set-        - use INLINABLE-        - test again with SPECIALIZE instead-        - try adding INLINE to all with higher-order args (or higher-order newtype wrappers)-           and make sure our LHS looks good for inlining-        - specialize `Action i (Behavior i)` or allow lots of unfolding... ? Optimize those loops, somehow. Rewrite rules?+        - first optimize TreeExample, by way of Benchmark.hs+        - criterion and profiling w/r/t lib.:+            - play with underlying Behavior Monad stack?+            - be more controlled about the source lists (do once before defaultMain), use 'evaluate'+            - run with +RTS -s and make sure everything is 0+            - see if case-based nil is better+            - try storing the same chan (observable sharing) in each node, and use for streaming +               send an MVar with messages for the query operation+            - get accurate baseline comparison between actors and set+            - use INLINABLE+            - test again with SPECIALIZE instead+            - try adding INLINE to all with higher-order args (or higher-order newtype wrappers)+               and make sure our LHS looks good for inlining+            - specialize `Action i (Behavior i)` or allow lots of unfolding... ? Optimize those loops, somehow. Rewrite rules?+            - look at "let floating" and INLINEABLE to get functions with "fully-applied (syntactically) LHS"+        - split-chan ChItem in heap profile -hy         - take a look at threadscope for random tree test-        - look at "let floating" and INLINEABLE to get functions with "fully-applied (syntactically) LHS"+        - forkOnIO to keep communicating actors on same HEC?         - compare with previous version (cp to /tmp to use previous version)+++Later:+    - make that also work with Behaviors of arbitrary input types using new GHC generics?+    - can we make joins work with arbitrary types using Generics?+    - can we support Either in Sources?     - get complete code coverage into simple test module     - interesting solution to exit detection:          http://en.wikipedia.org/wiki/Huang%27s_algorithm@@ -220,7 +228,7 @@         - a pre-declared Mailbox for IO?   Eventually:-    - some sort of exception handling technique (using actors?)+    - some sort of exception handling technique a.la erlang     - abilty to launch an actor that automatically "replicates" if its chan needs more        consumers. This should probably be restricted to an `Action i ()` that we        repeat.@@ -228,7 +236,7 @@       optimizing message flow with some algorithm?     - provide an "adapter" for amazon SQS, allowing truly distributed message       passing-    - investigate erlang-style selective receive (using Alternative?)+    - play w/ distributed-process (cloud haskell)     - consider: combining TChans, where values are popped off when available,       for chan-split?     - look at ways we can represent network IO as channels to interface with@@ -245,7 +253,7 @@         (maybe letting us use useful enumerators)      ...also now pipes, conduits, etc. etc. -     - study ambient/join/fusion calculi for clues as to where it's really at+     - study ambient/join/fusion calculi for clues to where it's really at   CHAN TYPES@@ -267,17 +275,30 @@ > runMailbox :: Mailbox a -> a -> IO () > runMailbox = getOp . sender >-> mkMailbox :: InChan a -> Mailbox a-> mkMailbox = mailbox . writeChan-> > mkMessages :: OutChan a -> Messages a > mkMessages = Messages . readChan > > -- | One can 'send' a messages to a @Mailbox@ where it will be processed > -- according to an actor\'s defined 'Behavior'+> --+> -- > type Joined (Mailbox a) = a > newtype Mailbox a = Mailbox { sender :: Sender a } >       deriving (Contravariant) ++Previously we were polymorphic in SplitChan in many places. Now that spawn+has polymorphic result type we simply export a function to convert from+any SplitChan type. Otherwise we'd have to provide type annotations everywhere.++I liked the previous version, since a send within an actor is semantically-+identical regardless of the channel type.++> -- | Convert the input side of a @SplitChan@ to a @Mailbox@. Useful for +> -- sending data out from an actor system via a channel created in IO.+> out :: (SplitChan i x)=> i a -> Mailbox a+> out = mailbox . writeChan++ 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:@@ -291,7 +312,7 @@ >     writeChan = runMailbox > > instance NewSplitChan Mailbox Messages where->     newSplitChan = (mkMailbox *** mkMessages) `fmap` newSplitChan+>     newSplitChan = (out *** mkMessages) `fmap` newSplitChan   For Mailboxes we can define all transformations associated with Cartesian and @@ -307,17 +328,22 @@ > zipMb :: Mailbox a -> Mailbox b -> Mailbox (a,b)  > zipMb m1 m2 = mailbox $ \(a,b) -> writeChan m1 a >> writeChan m2 b >-> -- | > productMb = contramap Left &&& contramap Right-> productMb :: Mailbox (Either a b) -> (Mailbox a, Mailbox b)-> productMb = contramap Left &&& contramap Right++The naming here doesn't make much sense now that these are general. Keep for +now and hope we can deprecate in favor of functionality in one of E.K.'s +libs?++> -- | > contraProduct = contramap Left &&& contramap Right+> contraProduct :: Contravariant f => f (Either a b) -> (f a, f b)+> contraProduct = contramap Left &&& contramap Right >-> -- | > faninMb f g = contramap (f ||| g)-> faninMb :: (a -> c) -> (b -> c)-> Mailbox c -> Mailbox (Either a b) -> faninMb f g = contramap (f ||| g)+> -- | > contraFanin f g = contramap (f ||| g)+> contraFanin :: Contravariant f => (b -> a) -> (c -> a) -> f a -> f (Either b c)+> contraFanin f g = contramap (f ||| g) >-> -- | > fanoutMb f g = contramap (f &&& g)-> fanoutMb :: (a -> b) -> (a -> c) -> Mailbox (b,c) -> Mailbox a-> fanoutMb f g = contramap (f &&& g)+> -- | > contraFanout f g = contramap (f &&& g)+> contraFanout :: Contravariant f=> (a -> b) -> (a -> c) -> f (b,c) -> f a+> contraFanout f g = contramap (f &&& g)   @@ -329,7 +355,7 @@ to import a bunch of libraries to get basic Behavior building functionality.  > infixl 3 <.|>-+> > -- | 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@.@@ -355,12 +381,12 @@ > -- | Useful to make defining a continuing Behavior more readable as a > -- \"receive block\", e.g. > ---> -- > pairUp out = Receive $ do+> -- > pairUpAndSendTo mb = Receive $ do > -- >     a <- received > -- >     receive $ do > -- >         b <- received-> -- >         send out (b,a)-> -- >         return (pairUp out)+> -- >         send mb (b,a)+> -- >         return (pairUpAndSendTo mb) > -- > -- Defined as:  > --@@ -381,18 +407,17 @@ > guardReceived :: (i -> Bool) -> Action i i > guardReceived p = ask >>= \i-> guard (p i) >> return i -> -- | 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 a message asynchronously to an actor receiving from Mailbox. See+> -- also 'out' for converting other types of chans to 'Mailbox'. > --   > -- > send b = liftIO . writeChan b-> send :: (MonadIO m, SplitChan c x)=> c a -> a -> m ()+> send :: (MonadIO m)=> Mailbox a -> a -> m () > send b = liftIO . writeChan b  > -- | A strict 'send': > -- > -- > send' b a = a `seq` send b a-> send' :: (MonadIO m, SplitChan c x)=> c a -> a -> m ()+> send' :: (MonadIO m)=> Mailbox a -> a -> m () > send' b a = a `seq` send b a  > infixr 1 <->@@ -402,7 +427,7 @@ > -- e.g. > -- > -- >     do mb <- 0 <-> spawn foo-> (<->) :: (MonadIO m, SplitChan c x)=> a -> m (c a) -> m (c a)+> (<->) :: (MonadIO m)=> a -> m (Mailbox a) -> m (Mailbox a) > a <-> mmb = mmb >>= \mb-> send mb a >> return mb  @@ -411,16 +436,213 @@ FORKING AND RUNNING ACTORS: =========================== +The strict Actor Model is limited in expressiveness, in that it doesn't allow+for a method of synchronization, e.g. we cannot have an actor that pairs up+incoming messages from two different channels. I think this leads to nonsense+like "selective receive" in Erlang (disclaimer: IANA erlang-xpert). -> -- | 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+I've realized that I can keep all the nice semantics of actors (i.e. this+change doesn't affect Behaviors) , while supporting synchronization and+simplifying the API all at the same time! This method is inspired by the "join+calculus", and I'm sure this isn't a new idea. +To support this elegantly in the API, we define a class with associated type,+and make 'spawn' the method. This allows the pattern of joins to be determined+polymorphically based on users' pattern match! ++    NOTE: My original goal was to use GHC.Generic to support arbitrary joins on+    any Generic a=> Behavior a ...but it wasn't coming together. Let me know+    if you can figure it out.+++> -- | We extend the actor model to support joining (or synchronizing) multiple+> -- 'Mailbox'es to a single 'Behavior' input type, using a new class with an+> -- associated type. Functionality is best explained by example:+> --+> -- Spawn an actor returning it's 'Mailbox', and send it its first message:+> -- +> -- > sumTuple :: Behavior (Int, Int)+> -- >+> -- > do b <- spawn sumTuple+> -- >    send b (4, 1) +> -- >    ...+> --+> -- But now we would like our @sumTuple@ actor to receive each number from a different +> -- concurrent actor:+> --+> -- > do (b1, b2) <- spawn sumTuple+> -- >    b3 <- spawn (multipliesBy2AndSendsTo b1)+> -- >    send b3 2+> -- >    send b2 1+> -- >    ...+> --+> -- Lastly spawn an actor that starts immediately on an infinite supply of @()@s,+> -- and supplies an endless stream of @Int@s to @sumTuple@+> --+> -- > do (b1, b2) <- spawn sumTuple+> -- >    () <- spawn (sendsIntsTo b2)+> -- >    send b1 4+> -- >    ...+> class Sources s where+>     type Joined s+>     newJoinedChan :: IO (s, Messages (Joined s)) -- private+++Spawn uses un-exported newJoinedChan where we used newSplitChan previously:++> -- | Fork an actor performing the specified 'Behavior'. /N.B./ an actor+> -- begins execution of its 'headBehavior' only after a message becomes+> -- available to process; for sending an initial message to an actor right+> -- after 'spawn'ing it, ('<|>') can be convenient.+> spawn :: (MonadIO m, Sources s)=> Behavior (Joined s) -> m s+> spawn b = liftIO $ do+>     (srcs, msgs) <- newJoinedChan+>     let runner b' = readChan msgs >>= runBehaviorStep b' >>= F.mapM_ runner+>     void $ forkIO (runner b)+>     return srcs+++...and our instance for Mailbox completes previous simple spawn functionality:++> instance Sources (Mailbox a) where+>     type Joined (Mailbox a) = a+>     newJoinedChan = newSplitChan+++By adding an instance for (,) synchronization and wonderful new things become+possible!++> instance (Sources a, Sources b)=> Sources (a,b) where+>     type Joined (a,b) = (Joined a, Joined b)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         let m' = Messages $ liftM2 (,) (readMsg ma) (readMsg mb)+>         return ((sa,sb), m')+++We'll add instances up to 7-tuples, since that seems to be standard, but people+can use nested tuples:++> instance (Sources a, Sources b, Sources c, Sources d, Sources e, Sources f, Sources g)=> Sources (a,b,c,d,e,f,g) where+>     type Joined (a,b,c,d,e,f,g) = (Joined a, Joined b,Joined c,Joined d,Joined e,Joined f,Joined g)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         (sc, mc) <- newJoinedChan+>         (sd, md) <- newJoinedChan+>         (se, me) <- newJoinedChan+>         (sf, mf) <- newJoinedChan+>         (sg, mg) <- newJoinedChan+>         let m' = Messages $ (,,,,,,) <$> readMsg ma <*> readMsg mb <*> readMsg mc <*> readMsg md <*> readMsg me <*> readMsg mf <*> readMsg mg +>         return ((sa,sb,sc,sd,se,sf,sg), m')+>+> instance (Sources a, Sources b, Sources c, Sources d, Sources e, Sources f)=> Sources (a,b,c,d,e,f) where+>     type Joined (a,b,c,d,e,f) = (Joined a, Joined b,Joined c,Joined d,Joined e,Joined f)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         (sc, mc) <- newJoinedChan+>         (sd, md) <- newJoinedChan+>         (se, me) <- newJoinedChan+>         (sf, mf) <- newJoinedChan+>         let m' = Messages $ (,,,,,) <$> readMsg ma <*> readMsg mb <*> readMsg mc <*> readMsg md <*> readMsg me <*> readMsg mf+>         return ((sa,sb,sc,sd,se,sf), m')+>+> instance (Sources a, Sources b, Sources c, Sources d, Sources e)=> Sources (a,b,c,d,e) where+>     type Joined (a,b,c,d,e) = (Joined a, Joined b,Joined c,Joined d,Joined e)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         (sc, mc) <- newJoinedChan+>         (sd, md) <- newJoinedChan+>         (se, me) <- newJoinedChan+>         let m' = Messages $ (,,,,) <$> readMsg ma <*> readMsg mb <*> readMsg mc <*> readMsg md <*> readMsg me+>         return ((sa,sb,sc,sd,se), m')+>+> instance (Sources a, Sources b, Sources c, Sources d)=> Sources (a,b,c,d) where+>     type Joined (a,b,c,d) = (Joined a, Joined b,Joined c,Joined d)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         (sc, mc) <- newJoinedChan+>         (sd, md) <- newJoinedChan+>         let m' = Messages $ (,,,) <$> readMsg ma <*> readMsg mb <*> readMsg mc <*> readMsg md +>         return ((sa,sb,sc,sd), m')+>+> instance (Sources a, Sources b, Sources c)=> Sources (a,b,c) where+>     type Joined (a,b,c) = (Joined a, Joined b,Joined c)+>     newJoinedChan = do+>         (sa, ma) <- newJoinedChan+>         (sb, mb) <- newJoinedChan+>         (sc, mc) <- newJoinedChan+>         let m' = Messages $ (,,) <$> readMsg ma <*> readMsg mb <*> readMsg mc +>         return ((sa,sb,sc), m')+++I give up for now on defining an instance for sums. This probably requires a+different formulation for class++    ...and we also support Either as a source, since this is the only way to get a joined+    product of sums; otherwise users could just use 'contraProduct', a pure operation.++    > -- | > type Joined (a :-: b) = Either (Joined a) (Joined b)+    > --+    > -- A product of 'Sources' corresponding to a @Behavior (Either a b)@. Allows+    > -- 'spawn'-ing a @Behavior@  which receives a sum of perhaps-'Joined' products.+    > --+    > -- See also: 'contraProduct'+    > data a :-: b = (:-:) { sourceLeft :: a+    >                      , sourceRight :: b }+    >+    > instance (Sources a, Sources b)=> Sources (a :-: b) where+    >     type Joined (a :-: b) = Either (Joined a) (Joined b)+    >     --newJoinedChan :: IO (a :-: b, Messages (Either (Joined a) (Joined b)))+    >     newJoinedChan = do+    >         (src, msgs) <- newSplitChan+    >         let (s1, s2) = contraProduct src+    >         return (decompose s1 :-: decompose s2, msgs)++    class Sources s where+        type Joined s :: *+        newJoinedChan :: IO (s, Messages (Joined s))+        decomp :: Mailbox (a,b) -> (Mailbox a, Mailbox b)+        decomp :: Mailbox a -> Mailbox a+        decomp :: Mailbox (Either a b) -> (Mailbox a :-: Mailbox b)+++We can subsume the old 'spawn_' functionality in our class as well, and imagine+returning an infinite source of ()s:++> -- | > type Joined () = ()+> --+> -- Represents an endless supply of @()@s. Allows 'spawn'-ing+> -- a @Behavior ()@ that starts immediately and loops until it 'yield'-s, e.g.+> -- +> -- > do () <- spawn startsImmediately -- :: Behavior ()+> instance Sources () where+>     type Joined () = ()+>     newJoinedChan = +>         return ((), Messages $ return ())++Replace polymorphic craziness with old spawn_ function, when we can:++> {-# RULES "spawn_" spawn = spawn_  #-}+> spawn_ :: (MonadIO m)=> Behavior () -> m ()+> spawn_ = liftIO . void . forkIO . runBehavior_+++    NOTE: spawnReading removed in 0.4, since it was unused (by me), exposed+    confusing implementation details, supports e.g. launching an actor on a+    bounded channel which violates the Model, and doesn't provide an effective+    way to do much cool stuff like reading from a network socket.++    Instead I guess we should expose enough internals in a separate module to+    support future cool stuff.+++ RUNNING ACTORS -------------- @@ -429,7 +651,7 @@ > -- | Run a @Behavior ()@ in the main thread, returning when the computation > -- exits. > runBehavior_ :: Behavior () -> IO ()-> runBehavior_ b = runBehavior b [(),()..]+> runBehavior_ b = runBehavior b $ repeat () > > -- | run a 'Behavior' in the IO monad, taking its \"messages\" from the list. > runBehavior :: Behavior a -> [a] -> IO ()@@ -438,29 +660,7 @@   -FORKING ACTORS--------------- -> -- | Fork an actor performing the specified 'Behavior'. /N.B./ an actor -> -- begins execution of its 'headBehavior' only after a message has been -> -- received; for sending an initial message to an actor right after 'spawn'ing-> -- it, ('<|>') can be convenient.-> ---> -- See also 'spawn_'.-> spawn :: (MonadIO m)=> Behavior i -> m (Mailbox i)-> spawn b = do->     (m,s) <- liftIO newSplitChan->     spawnReading s b->     return m->-> -- | 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_  --- USEFUL GENERAL BEHAVIORS ======================== @@ -493,7 +693,7 @@ > -- signalling the end of some other 'Behavior'. > -- > -- > signalB c = Receive (send c () >> yield)-> signalB :: (SplitChan c x)=> c () -> Behavior i+> signalB :: Mailbox () -> Behavior i > signalB c = Receive (send c () >> yield)  > -- | A @Behavior@ that discard its first input, returning the passed Behavior
simple-actors.cabal view
@@ -7,12 +7,12 @@ -- 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.3.0+Version:             0.4.0  -- A short (one-line) description of the package. 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/+Homepage:            http://brandon.si/code/simple-actors-0-1-0-released/  -- A longer description of the package Description:         simple-actors is an EDSL-style library for writing@@ -21,15 +21,23 @@                      single input value, perform some 'Action's, and return the                      Behavior to process the next input message it receives.                      .-                     /CHANGES/ 0.2.1 - 0.3:+                     We also extend the strict actor model in various elegant ways.+                     See source for this type of discussion.                      .-                     - added (@\<-\>@) convenience operator, and strict @send'@+                     /CHANGES/: 0.3.0 - 0.4                      .-                     - add composition\/transformation functions for Mailboxes+                     - extend 'Control.Concurrent.Actors.spawn' to transparently support "joined+                       mailboxes", i.e. join-patterns, supporting synchronization between actors.+                       'Control.Concurrent.Actors.spawn' becomes polymorphic in new 'Control.Concurrent.Actors.Sources' class                      .-                     - drop support for older base and transformers, no more CPP+                     - old @spawn_@ subsumed by polymorphic @spawn@, with @()@                      .-                     - depend on chan-split 0.4+                     - @send@ and other send-like functions restricted to Mailboxes, use 'out' to convert arbitrary chans to Mailboxes+                     .+                     - productMb, faninMb, and fanoutMb renamed 'Control.Concurrent.Actors.contraProduct', 'Control.Concurrent.Actors.contraFanin', and 'Control.Concurrent.Actors.contraFanout', and given more general types+                     .+                     - old @spawnReading@ removed+                     .   -- The license under which the package is released.@@ -73,9 +81,9 @@   -- Packages needed in order to build this package.   Build-depends:       base >= 4.3 && < 5                          , chan-split >= 0.4.0-                     , mtl >= 2+                     , mtl >= 2.1.1                      , transformers >= 0.3-                     , contravariant+                     , contravariant >= 0.2.0.1                           ghc-options:        -Wall