diff --git a/Control/Concurrent/CHP.hs b/Control/Concurrent/CHP.hs
--- a/Control/Concurrent/CHP.hs
+++ b/Control/Concurrent/CHP.hs
@@ -40,6 +40,8 @@
 --
 -- * "Control.Concurrent.CHP.Common"
 --
+-- * "Control.Concurrent.CHP.Connect"
+-- 
 -- * "Control.Concurrent.CHP.Console"
 --
 -- * "Control.Concurrent.CHP.Test"
diff --git a/Control/Concurrent/CHP/Barriers.hs b/Control/Concurrent/CHP/Barriers.hs
--- a/Control/Concurrent/CHP/Barriers.hs
+++ b/Control/Concurrent/CHP/Barriers.hs
@@ -64,9 +64,8 @@
 -- may query the current phase for any barrier that they are currently enrolled
 -- on.
 module Control.Concurrent.CHP.Barriers (Barrier, EnrolledBarrier, newBarrier, newBarrierWithLabel,
-  PhasedBarrier, newPhasedBarrier, newPhasedBarrierWithLabel, newPhasedBarrierCustomInc,
-    newPhasedBarrierCustomShowInc, newPhasedBarrierWithLabelCustomInc,
-    newPhasedBarrierWithLabelCustomShowInc, currentPhase, waitForPhase,
+  PhasedBarrier, newPhasedBarrier, newPhasedBarrier', BarOpts(..), defaultIncPhase, defaultBarOpts,
+    barLabel, currentPhase, waitForPhase, syncAndWaitForPhase,
     syncBarrier, getBarrierIdentifier) where
 
 import Control.Concurrent.STM
@@ -109,6 +108,46 @@
                        when (ph /= phCur) $
                          repeatUntil (== ph) (syncBarrier b)
 
+-- | Synchronises on the barrier at least once, until it is in the given phase.
+--
+-- Note that @syncAndWaitForPhase ph bar == syncBarrier bar >> waitForPhase ph
+-- bar@.
+--
+-- Added in version 1.7.0.
+syncAndWaitForPhase :: Eq phase => phase -> Enrolled PhasedBarrier phase -> CHP ()
+syncAndWaitForPhase ph = repeatUntil (== ph) . syncBarrier
+
+-- | Options for barrier creation; a function to show the inner data, and an optional
+-- label (both only affect tracing).  These options can be passed to newPhasedBarrier'.
+--
+-- Added in version 1.7.0.
+data BarOpts phase = BarOpts { barIncPhase :: phase -> phase
+  -- ^ Aside from the standard 'defaultIncPhase', you can use succ or (+1) with
+  -- Integer as the inner type to get a barrier that never cycles.  You can also
+  -- do things like supplying (+2) as the incrementing function, or even using
+  -- lists as the phase type to do crazy things.
+  , barOptsShow :: phase -> String, barOptsLabel :: Maybe String }
+
+-- | The default phase incrementing function.  If the phase is already at 'maxBound',
+-- it sets it to 'minBound'; otherwise it uses 'succ' to increment the phase.
+defaultIncPhase :: (Enum phase, Bounded phase, Eq phase) => phase -> phase
+defaultIncPhase p
+  | p == maxBound = minBound
+  | otherwise = succ p
+
+-- | The default: don't show anything, don't label anything, use Enum+Bounded+Eq to
+-- work out the phase increment.
+-- 
+-- Added in version 1.7.0.
+defaultBarOpts :: (Enum phase, Bounded phase, Eq phase) => BarOpts phase
+defaultBarOpts = BarOpts defaultIncPhase (const "") Nothing
+
+-- | Uses the Show instance for showing the data in traces, and the given label.
+--
+-- Added in version 1.7.0.
+barLabel :: (Enum phase, Bounded phase, Eq phase, Show phase) => String -> BarOpts phase
+barLabel = BarOpts defaultIncPhase show . Just
+
 -- | Creates a new barrier with no processes enrolled
 newBarrier :: CHP Barrier
 newBarrier = newPhasedBarrier ()
@@ -122,84 +161,23 @@
 --
 -- The Show constraint was added in version 1.5.0
 newPhasedBarrier :: (Enum phase, Bounded phase, Eq phase, Show phase) => phase -> CHP (PhasedBarrier phase)
-newPhasedBarrier ph = liftPoison $ liftTrace $ do
-  tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent show tv 
-  return $ Barrier (e, tv, \p -> if p == maxBound then minBound else succ p)
-
--- | Creates a new barrier with no processes enrolled, that will be on the
--- given phase, along with a custom function to increment the phase.  You can therefore
--- use this function with Integer as the inner type (and succ or (+1) as the incrementing
--- function) to get a barrier that never cycles.  You can also do things like supplying
--- (+2) as the incrementing function, or even using lists as the phase type to
--- do crazy things.
---
--- Note that the phase will not show up in the traces -- see
--- 'newPhasedBarrierCustomShowInc' for that.
-newPhasedBarrierCustomInc :: (phase -> phase) -> phase -> CHP (PhasedBarrier phase)
-newPhasedBarrierCustomInc f ph = liftPoison $ liftTrace $ do
-  tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent (const "") tv
-  return $ Barrier (e, tv, f)
+newPhasedBarrier ph = newPhasedBarrier' ph $ BarOpts defaultIncPhase show Nothing
 
--- | Creates a new barrier with no processes enrolled, that will be on the
--- given phase, along with a custom function to show the phase in traces and to
--- increment the phase.  You can therefore
--- use this function with Integer as the inner type (and succ or (+1) as the incrementing
--- function, and show as the showing function) to get a barrier that never cycles.  You can also do things like supplying
--- (+2) as the incrementing function, or even using lists as the phase type to
--- do crazy things.
---
--- This function was added in version 1.5.0.
-newPhasedBarrierCustomShowInc :: (phase -> String) -> (phase -> phase) -> phase -> CHP (PhasedBarrier phase)
-newPhasedBarrierCustomShowInc sh f ph = liftPoison $ liftTrace $ do
+-- | Like 'newPhasedBarrier' but allows you to customise the options.
+newPhasedBarrier' :: phase -> BarOpts phase -> CHP (PhasedBarrier phase)
+newPhasedBarrier' ph (BarOpts incPh showPh label) = liftPoison $ liftTrace $ do
   tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent sh tv
-  return $ Barrier (e, tv, f)
-
-
+  e <- liftIO $ newBarrierEvent showPh tv 
+  maybe (return ()) (labelEvent e) label
+  return $ Barrier (e, tv, incPh)
 
 -- | Creates a new barrier with no processes enrolled and labels it in traces
 -- using the given label.  See 'newBarrier'.
 newBarrierWithLabel :: String -> CHP Barrier
-newBarrierWithLabel l = newPhasedBarrierWithLabel l ()
-
--- | Creates a new barrier with no processes enrolled and labels it in traces
--- using the given label.  See 'newPhasedBarrier'.
---
--- The Show constraint was added in version 1.5.0.
-newPhasedBarrierWithLabel :: (Enum phase, Bounded phase, Eq phase, Show phase) => String -> phase -> CHP (PhasedBarrier phase)
-newPhasedBarrierWithLabel l ph = liftPoison $ liftTrace $ do
-  tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent show tv
-  labelEvent e l
-  return $ Barrier (e, tv, \p -> if p == maxBound then minBound else succ p)
-
--- | Creates a new barrier with no processes enrolled and labels it in traces
--- using the given label.  See 'newPhasedBarrierCustomInc'.
---
--- Note that the barrier will not record the phase in the traces -- see
--- 'newPhasedBarrierWithLabelCustomShowInc' for that.
-newPhasedBarrierWithLabelCustomInc :: String -> (phase -> phase) -> phase -> CHP (PhasedBarrier phase)
-newPhasedBarrierWithLabelCustomInc l f ph = liftPoison $ liftTrace $ do
-  tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent (const "") tv
-  labelEvent e l
-  return $ Barrier (e, tv, f)
-
--- | Creates a new barrier with no processes enrolled and labels it in traces
--- using the given label and given show function for the phase.  See 'newPhasedBarrierWithLabelCustomInc'.
---
--- This function was added in version 1.5.0.
-newPhasedBarrierWithLabelCustomShowInc :: String -> (phase -> String) -> (phase -> phase) -> phase -> CHP (PhasedBarrier phase)
-newPhasedBarrierWithLabelCustomShowInc l sh f ph = liftPoison $ liftTrace $ do
-  tv <- liftIO $ atomically $ newTVar ph
-  e <- liftIO $ newBarrierEvent sh tv
-  labelEvent e l
-  return $ Barrier (e, tv, f)
-
+newBarrierWithLabel = newPhasedBarrier' () . barLabel
 
 -- | Gets the identifier of a Barrier.  Useful if you want to identify it in
 -- the trace later on.
 getBarrierIdentifier :: PhasedBarrier ph -> Unique
 getBarrierIdentifier (Barrier (e, _, _)) = getEventUnique e
+
diff --git a/Control/Concurrent/CHP/Channels/BroadcastReduce.hs b/Control/Concurrent/CHP/Channels/BroadcastReduce.hs
--- a/Control/Concurrent/CHP/Channels/BroadcastReduce.hs
+++ b/Control/Concurrent/CHP/Channels/BroadcastReduce.hs
@@ -89,7 +89,7 @@
 
 -- | The Eq instance was added in version 1.4.0.
 --
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 newtype BroadcastChannel a = BC (Barrier, TVar (Maybe a), ManyToOneTVar Int)
 
@@ -173,14 +173,14 @@
 
 -- | Added in version 1.5.0.
 --
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 oneToManyChannel' :: MonadCHP m => ChanOpts a -> m (OneToManyChannel a)
 oneToManyChannel' = newChannel'
 
 -- | Added in version 1.5.0.
 --
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 anyToManyChannel' :: MonadCHP m => ChanOpts a -> m (AnyToManyChannel a)
 anyToManyChannel' = newChannel'
@@ -188,7 +188,7 @@
 
 -- | The Eq instance was added in version 1.4.0.
 -- 
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 newtype ReduceChannel a = GC (Barrier, ManyToOneTVar (Int, Maybe (a, TVar Bool)), (a -> a -> a, a))
 
@@ -276,14 +276,14 @@
 
 -- | Added in version 1.5.0.
 -- 
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 manyToOneChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToOneChannel a)
 manyToOneChannel' = const manyToOneChannel --TODO
 
 -- | Added in version 1.5.0.
 -- 
--- In versions 1.5.0-1.6.0, the broadcast and reduce channels do not appear correctly
+-- In versions 1.5.0-1.7.0, the broadcast and reduce channels do not appear correctly
 -- in the traces.
 manyToAnyChannel' :: (Monoid a, MonadCHP m) => ChanOpts a -> m (ManyToAnyChannel a)
 manyToAnyChannel' = const manyToAnyChannel --TODO
diff --git a/Control/Concurrent/CHP/Connect.hs b/Control/Concurrent/CHP/Connect.hs
new file mode 100644
--- /dev/null
+++ b/Control/Concurrent/CHP/Connect.hs
@@ -0,0 +1,185 @@
+-- Communicating Haskell Processes.
+-- Copyright (c) 2008--2009, University of Kent.
+-- All rights reserved.
+-- 
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions are
+-- met:
+--
+--  * Redistributions of source code must retain the above copyright
+--    notice, this list of conditions and the following disclaimer.
+--  * 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.
+--  * Neither the name of the University of Kent nor the names of its
+--    contributors may be used to endorse or promote products derived from
+--    this software without specific prior written permission.
+--
+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
+
+-- | A module of operators for connecting processes together.
+--
+-- This whole module was added in version 1.7.0.
+module Control.Concurrent.CHP.Connect
+  (Connectable(..), (<=>), (|<=>), (<=>|), (|<=>|), pipelineConnect, pipelineConnectComplete,
+    cycleConnect, ConnectableExtra(..), connectWith) where
+
+import Control.Applicative
+import Control.Arrow
+
+import Control.Concurrent.CHP
+
+-- | Like 'Connectable', but allows an extra parameter.
+--
+-- The API (and name) for this is still in flux, so do not rely on it just yet.
+class ConnectableExtra l r where
+  type ConnectableParam l
+  -- | Runs the given code with the two items connected.
+  connectExtra :: ConnectableParam l -> ((l, r) -> CHP ()) -> CHP ()
+
+-- | Indicates that its two parameters can be joined together automatically.
+--
+-- Rather than use 'connect' directly, you will want to use the operators such
+-- as '(<=>)'.  There are different forms of this operator for in the middle of
+-- a pipeline (where you still need further parameters to each process), and at
+-- the ends.  See also 'pipelineConnect' and 'pipelineConnectComplete'.
+class Connectable l r where
+  -- | Runs the given code with the two items connected.
+  connect :: ((l, r) -> CHP ()) -> CHP ()
+
+-- | Joins together the given two processes and runs them in parallel.
+(|<=>|) :: Connectable l r => (l -> CHP ()) -> (r -> CHP ()) -> CHP ()
+(|<=>|) p q = connect $ \(x, y) -> p x <|*|> q y
+
+jpo :: ConnectableExtra l r => ConnectableParam l -> (l -> CHP ()) -> (r -> CHP ()) -> CHP ()
+jpo o p q = connectExtra o $ \(x, y) -> p x <|*|> q y
+
+-- | Joins together the given two processes and runs them in parallel.
+(<=>) :: Connectable l r => (a -> l -> CHP ()) -> (r -> b -> CHP ()) -> a -> b -> CHP ()
+(<=>) p q x y = p x |<=>| flip q y
+
+-- | Joins together the given two processes and runs them in parallel.
+(<=>|) :: Connectable l r => (a -> l -> CHP ()) -> (r -> CHP ()) -> a -> CHP ()
+(<=>|) p q x = p x |<=>| q
+
+-- | Joins together the given two processes and runs them in parallel.
+(|<=>) :: Connectable l r => (l -> CHP ()) -> (r -> b -> CHP ()) -> b -> CHP ()
+(|<=>) p q x = p |<=>| flip q x
+
+-- | Like '(<=>)' but with 'ConnectableExtra'
+connectWith :: ConnectableExtra l r => ConnectableParam l ->
+  (a -> l -> CHP ()) -> (r -> b -> CHP ()) -> a -> b -> CHP ()
+connectWith o p q x y = jpo o (p x) (flip q y)
+
+-- | Like @foldl1 (<=>)@; connects a pipeline of processes together.  If the list
+-- is empty, it returns a process that ignores both its arguments and returns instantly.
+pipelineConnect :: Connectable l r => [r -> l -> CHP ()] -> r -> l -> CHP ()
+pipelineConnect [] = const . const $ return ()
+pipelineConnect [p] = p
+pipelineConnect (p:ps) = p <=> pipelineConnect ps
+
+-- | Connects the given beginning process, the list of middle processes, and
+-- the end process into a pipeline and runs them all in parallel.  If the list
+-- is empty, it connects the beginning directly to the end.
+pipelineConnectComplete :: Connectable l r =>
+  (l -> CHP ()) -> [r -> l -> CHP ()] -> (r -> CHP ()) -> CHP ()
+pipelineConnectComplete begin [] end = begin |<=>| end
+pipelineConnectComplete begin middle end
+  = (begin |<=> pipelineConnect middle) |<=>| end
+
+-- | Like 'pipelineConnect' but also connects the last process into the first.
+--  If the list is empty, it returns immediately.
+cycleConnect :: Connectable l r => [r -> l -> CHP ()] -> CHP ()
+cycleConnect [] = return ()
+cycleConnect ps = connect . uncurry . flip . pipelineConnect $ ps
+
+instance Connectable (Chanout a) (Chanin a) where
+  connect = (>>=) ((writer &&& reader) <$> oneToOneChannel)
+instance ConnectableExtra (Chanout a) (Chanin a) where
+  type ConnectableParam (Chanout a) = ChanOpts a
+  connectExtra o = (>>=) ((writer &&& reader) <$> oneToOneChannel' o)
+
+instance Connectable (Chanin a) (Chanout a) where
+  connect = (>>=) ((reader &&& writer) <$> oneToOneChannel)
+instance ConnectableExtra (Chanin a) (Chanout a) where
+  type ConnectableParam (Chanin a) = ChanOpts a
+  connectExtra o = (>>=) ((reader &&& writer) <$> oneToOneChannel' o)
+
+instance Connectable (Enrolled PhasedBarrier ()) (Enrolled PhasedBarrier ()) where
+  connect m = do b <- newBarrier
+                 enroll b $ \b0 -> enroll b $ \b1 -> m (b0, b1)
+
+instance ConnectableExtra (Enrolled PhasedBarrier ph) (Enrolled PhasedBarrier ph) where
+  type ConnectableParam (Enrolled PhasedBarrier ph) = (ph, BarOpts ph)
+  connectExtra (ph, o) m
+    = do b <- newPhasedBarrier' ph o
+         enroll b $ \b0 -> enroll b $ \b1 -> m (b0, b1)
+
+
+instance (Connectable al ar, Connectable bl br) => Connectable (al, bl) (ar, br) where
+  connect m = connect $ \(ax, ay) -> connect $ \(bx, by) -> m ((ax, bx), (ay, by))
+instance (ConnectableExtra al ar, ConnectableExtra bl br) => ConnectableExtra (al, bl) (ar, br) where
+  type ConnectableParam (al, bl) = (ConnectableParam al, ConnectableParam bl)
+  connectExtra (ao, bo) m = connectExtra ao $ \(ax, ay) -> connectExtra bo $ \(bx, by) -> m ((ax, bx), (ay, by))
+
+instance (Connectable al ar, Connectable bl br, Connectable cl cr) =>
+          Connectable (al, bl, cl) (ar, br, cr) where
+  connect m = connect $ \(ax, ay) -> connect $ \(bx, by) ->
+              connect $ \(cx, cy) -> m ((ax, bx, cx), (ay, by, cy))
+
+instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr) =>
+  ConnectableExtra (al, bl, cl) (ar, br, cr) where
+  type ConnectableParam (al, bl, cl) = (ConnectableParam al, ConnectableParam bl, ConnectableParam cl)
+  connectExtra (ao, bo, co) m
+    = connectExtra ao $ \(ax, ay) -> connectExtra bo $ \(bx, by) ->
+      connectExtra co $ \(cx, cy) -> m ((ax, bx, cx), (ay, by, cy))
+
+instance (Connectable al ar, Connectable bl br, Connectable cl cr,
+          Connectable dl dr) =>
+          Connectable (al, bl, cl, dl) (ar, br, cr, dr) where
+  connect m = connect $ \(ax, ay) -> connect $ \(bx, by) ->
+              connect $ \(cx, cy) -> connect $ \(dx, dy) ->
+                m ((ax, bx, cx, dx), (ay, by, cy, dy))
+instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr,
+          ConnectableExtra dl dr) =>
+          ConnectableExtra (al, bl, cl, dl) (ar, br, cr, dr) where
+  type ConnectableParam (al, bl, cl, dl)
+    = (ConnectableParam al,
+       ConnectableParam bl,
+       ConnectableParam cl,
+       ConnectableParam dl)
+  connectExtra (ao, bo, co, do_) m
+    = connectExtra ao $ \(ax, ay) -> connectExtra bo $ \(bx, by) ->
+      connectExtra co $ \(cx, cy) -> connectExtra do_ $ \(dx, dy) ->
+        m ((ax, bx, cx, dx), (ay, by, cy, dy))
+
+instance (Connectable al ar, Connectable bl br, Connectable cl cr,
+          Connectable dl dr, Connectable el er) =>
+          Connectable (al, bl, cl, dl, el) (ar, br, cr, dr, er) where
+  connect m = connect $ \(ax, ay) -> connect $ \(bx, by) ->
+              connect $ \(cx, cy) -> connect $ \(dx, dy) ->
+              connect $ \(ex, ey) -> m ((ax, bx, cx, dx, ex), (ay, by, cy, dy, ey))
+instance (ConnectableExtra al ar, ConnectableExtra bl br, ConnectableExtra cl cr,
+          ConnectableExtra dl dr, ConnectableExtra el er) =>
+          ConnectableExtra (al, bl, cl, dl, el) (ar, br, cr, dr, er) where
+  type ConnectableParam (al, bl, cl, dl, el)
+    = (ConnectableParam al,
+       ConnectableParam bl,
+       ConnectableParam cl,
+       ConnectableParam dl,
+       ConnectableParam el)
+  connectExtra (ao, bo, co, do_, eo) m
+    = connectExtra ao $ \(ax, ay) -> connectExtra bo $ \(bx, by) ->
+      connectExtra co $ \(cx, cy) -> connectExtra do_ $ \(dx, dy) ->
+        connectExtra eo $ \(ex, ey) -> m ((ax, bx, cx, dx, ex), (ay, by, cy, dy, ey))
+
diff --git a/Control/Concurrent/CHP/Enroll.hs b/Control/Concurrent/CHP/Enroll.hs
--- a/Control/Concurrent/CHP/Enroll.hs
+++ b/Control/Concurrent/CHP/Enroll.hs
@@ -31,9 +31,10 @@
 -- channels).
 
 module Control.Concurrent.CHP.Enroll (Enrolled, Enrollable(..), furtherEnroll,
-  enrollPair, enrollList) where
+  enrollPair, enrollList, enrollAll, enrollAll_) where
 
 import Control.Concurrent.CHP.Base
+import Control.Concurrent.CHP.Parallel
 
 class Enrollable b z where
   -- | Enrolls on the given barrier, then executes the given function (passing
@@ -75,3 +76,19 @@
 enrollList [] f = f []
 enrollList (b:bs) f = enroll b $ \eb -> enrollList bs $ \ebs -> f (eb:ebs)
 
+-- | Given a command to allocate a new barrier, and a list of processes that use
+-- that barrier, enrolls the appropriate number of times (i.e. the list length)
+-- and runs all the processes in parallel using that barrier, then returns a list
+-- of the results.
+--
+-- If you have already allocated the barrier, pass @return bar@ as the first parameter.
+--
+-- Added in version 1.7.0.
+enrollAll :: Enrollable b p => CHP (b p) -> [Enrolled b p -> CHP a] -> CHP [a]
+enrollAll mbar ps = mbar >>= flip enrollList (runParallel . zipWith ($) ps) . replicate (length ps)
+
+-- | Like enrollAll, but discards the results.
+--
+-- Added in version 1.7.0.
+enrollAll_ :: Enrollable b p => CHP (b p) -> [Enrolled b p -> CHP a] -> CHP ()
+enrollAll_ mbar ps = mbar >>= flip enrollList (runParallel_ . zipWith ($) ps) . replicate (length ps)
diff --git a/Control/Concurrent/CHP/Utils.hs b/Control/Concurrent/CHP/Utils.hs
--- a/Control/Concurrent/CHP/Utils.hs
+++ b/Control/Concurrent/CHP/Utils.hs
@@ -35,6 +35,11 @@
 -- has channels going in both directions rather than just one, 'dualPipeline' and/or
 -- '|<->|' can be used.  Several other variants on these functions are also provided,
 -- including operators to use at the beginning and ends of pipelines.
+--
+-- Most of the functions in this module are superseded or generalised by those
+-- in the "Control.Concurrent.CHP.Connect" module since version 1.7.0, so you should
+-- look to use those connectors rather than these, as the connects in this module
+-- may be removed at some point in the future.
 module Control.Concurrent.CHP.Utils where
 
 import Control.Monad
@@ -259,3 +264,4 @@
     useBar :: EnrolledBarrier -> CHP a -> CHP a
     useBar b p = (p <-> (syncBarrier b >> throwPoison)) `onPoisonRethrow` (poison b)
 -}
+
diff --git a/chp.cabal b/chp.cabal
--- a/chp.cabal
+++ b/chp.cabal
@@ -1,5 +1,5 @@
 Name:            chp
-Version:         1.6.0
+Version:         1.7.0
 Synopsis:        An implementation of concurrency ideas from Communicating Sequential Processes
 License:         BSD3
 License-file:    LICENSE
@@ -19,6 +19,7 @@
 Homepage:        http://www.cs.kent.ac.uk/projects/ofa/chp/
 Category:        Concurrency
 
+Cabal-Version:   >= 1.2.3
 Build-Type:      Simple
 Build-Depends:   base >= 3 && < 5, extensible-exceptions >= 0.1.1.0, containers, HUnit, mtl, parallel, pretty, QuickCheck >= 2, stm
 
@@ -38,6 +39,7 @@
                  Control.Concurrent.CHP.Channels.Synonyms
                  Control.Concurrent.CHP.Clocks
                  Control.Concurrent.CHP.Common
+                 Control.Concurrent.CHP.Connect
                  Control.Concurrent.CHP.Console
                  Control.Concurrent.CHP.Enroll
                  Control.Concurrent.CHP.Monad
@@ -61,7 +63,7 @@
                  Control.Concurrent.CHP.Traces.Base
 
 Extensions:      ScopedTypeVariables MultiParamTypeClasses
-                 FlexibleInstances
+                 FlexibleInstances TypeFamilies
                  GeneralizedNewtypeDeriving CPP BangPatterns
 
 GHC-Options:     -Wall -auto-all
