diff --git a/Control/Concurrent/Actors.lhs b/Control/Concurrent/Actors.lhs
--- a/Control/Concurrent/Actors.lhs
+++ b/Control/Concurrent/Actors.lhs
@@ -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
diff --git a/simple-actors.cabal b/simple-actors.cabal
--- a/simple-actors.cabal
+++ b/simple-actors.cabal
@@ -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
