diff --git a/Control/Pipe.hs b/Control/Pipe.hs
--- a/Control/Pipe.hs
+++ b/Control/Pipe.hs
@@ -1,6 +1,8 @@
 {-|
     'Pipe' is a monad transformer that enriches the base monad with the ability
     to 'await' or 'yield' data to and from other 'Pipe's.
+
+    For an extended tutorial, consult "Control.Pipe.Tutorial".
 -}
 
 module Control.Pipe (
@@ -63,12 +65,11 @@
 -}
 
 -- | The base functor for the 'Pipe' type
-data PipeF a b x = Await (a -> x) | Yield (b, x)
+data PipeF a b x = Await (a -> x) | Yield b x
 
--- I could use the "DerivingFunctor" extension, but I want to remain portable
 instance Functor (PipeF a b) where
-    fmap f (Await a) = Await $ fmap f a
-    fmap f (Yield y) = Yield $ fmap f y
+    fmap f (Await   g) = Await (f . g)
+    fmap f (Yield b x) = Yield b (f x)
 
 {-|
     The base type for pipes
@@ -122,12 +123,12 @@
     'yield' restores control back upstream and binds the result to 'await'.
 -}
 yield :: (Monad m) => b -> Pipe a b m ()
-yield b = wrap $ Yield (b, return ())
+yield b = wrap $ Yield b (return ())
 
 {-|
     Convert a pure function into a pipe
 
-> pipe = forever $ do
+> pipe f = forever $ do
 >     x <- await
 >     yield (f x)
 -}
@@ -202,13 +203,13 @@
     let p1' = FreeT $ return x1
     runFreeT $ case x1 of
         Pure r          -> return r
-        Free (Yield y ) -> wrap $ Yield $ fmap (<+< p2) y
+        Free (Yield b p1') -> wrap $ Yield b $ p1' <+< p2
         Free (Await f1) -> FreeT $ do
             x2 <- runFreeT p2
             runFreeT $ case x2 of
-                Pure r            -> return r
-                Free (Yield (x, p)) -> f1 x <+< p
-                Free (Await f2    ) -> wrap $ Await $ fmap (p1' <+<) f2
+                Pure r             -> return r
+                Free (Yield b p2') -> f1 b <+< p2'
+                Free (Await   f2 ) -> wrap $ Await $ \a -> p1' <+< f2 a
 
 -- | Corresponds to ('>>>') from @Control.Category@
 (>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
@@ -269,5 +270,5 @@
     e <- runFreeT p
     case e of
         Pure r         -> return r
-        Free (Await f) -> runPipe $ f ()
-        Free (Yield y) -> runPipe $ snd y
+        Free (Await   f) -> runPipe $ f ()
+        Free (Yield _ p) -> runPipe p
diff --git a/Control/Proxy.hs b/Control/Proxy.hs
new file mode 100644
--- /dev/null
+++ b/Control/Proxy.hs
@@ -0,0 +1,331 @@
+{-| A 'Proxy' 'request's input from upstream and 'respond's with output to
+    downstream.
+
+    For an extended tutorial, consult "Control.Proxy.Tutorial". -}
+
+module Control.Proxy (
+    -- * Types
+    -- $types
+    ProxyF(..),
+    Proxy,
+    Server,
+    Client,
+    Session,
+    -- * Build Proxies
+    -- $build
+    request,
+    respond,
+    -- * Compose Proxies
+    -- $compose
+    (<-<),
+    (>->),
+    idT,
+    -- * Run Sessions 
+    -- $run
+    runSession,
+    -- * Utility functions
+    -- $utility
+    discard,
+    ignore,
+    foreverK,
+    -- * Pipe compatibility
+    -- $pipe
+    Pipe,
+    Producer,
+    Consumer,
+    Pipeline,
+    await,
+    yield,
+    pipe,
+    (<+<),
+    (>+>),
+    idP,
+    runPipe
+    ) where
+
+import Control.Monad
+import Control.Monad.Trans.Class
+import Control.Monad.Trans.Free
+import Data.Void
+
+-- Imports for Haddock links
+import Control.Category ((<<<), (>>>), id, (.))
+import Prelude hiding ((.), id)
+
+{- $types
+    A 'Proxy' communicates with an upstream interface and a downstream
+    interface.
+
+    The type variables of @Proxy req_a resp_a req_b resp_b m r@ signify:
+
+    * @req_a @ - The request supplied to the upstream interface
+
+    * @resp_a@ - The response provided by the upstream interface
+
+    * @req_b @ - The request supplied by the downstream interface
+
+    * @resp_b@ - The response provided to the downstream interface
+
+    * @m     @ - The base monad
+
+    * @r     @ - The final return value -}
+
+-- | The base functor for the 'Proxy' type
+data ProxyF a' a b' b x = Request a' (a -> x) | Respond b (b' -> x)
+
+instance Functor (ProxyF a' a b' b) where
+    fmap f (Respond b  fb') = Respond b  (f . fb')
+    fmap f (Request a' fa ) = Request a' (f . fa )
+
+-- | A 'Proxy' converts one interface to another
+type Proxy a' a b' b = FreeT (ProxyF a' a b' b)
+
+{-| @Server req resp@ receives requests of type @req@ and sends responses of
+    type @resp@.
+
+    'Server's only 'respond' and never 'request' anything. -}
+type Server req resp = Proxy Void   () req resp
+
+{-| @Client req resp@ sends requests of type @req@ and receives responses of
+    type @resp@.
+
+    'Client's only 'request' and never 'respond' to anything. -}
+type Client req resp = Proxy  req resp () Void
+
+{-| A self-contained 'Session', ready to be run by 'runSession'
+
+    'Session's never 'request' anything or 'respond' to anything. -}
+type Session         = Proxy Void   ()  () Void
+
+{- $build
+    @Proxy@ forms both a monad and a monad transformer.  This means you can
+    assemble a 'Proxy' using @do@ notation using only 'request', 'respond', and
+    'lift':
+
+> truncate :: Int -> Int -> Proxy Int ByteString Int ByteString IO r
+> truncate maxBytes bytes = do
+>     when (bytes > maxBytes) $ lift $ putStrLn "Input truncated"
+>     bs <- request (min bytes maxBytes)
+>     bytes' <- respond bs
+>     truncate maxBytes bytes'
+
+    You define a 'Proxy' as a function of its initial input (@bytes@ in the
+    above example), and subsequent inputs are bound by the 'respond' command.
+-}
+
+{-| 'request' input from upstream, passing an argument with the request
+
+    @request a'@ passes @a'@ as a parameter to upstream that upstream can use to
+    decide what response to return.  'request' binds the response to its return
+    value. -}
+request :: (Monad m) => a' -> Proxy a' a b' b m a
+request a' = liftF $ Request a' id
+
+{-| 'respond' with an output for downstream and bind downstream's next 'request'
+
+    @respond b@ satisfies a downstream 'request' by supplying the value @b@.
+    'respond' blocks until downstream 'request's a new value and binds the
+    argument from the next 'request' as its return value. -}
+respond :: (Monad m) => b  -> Proxy a' a b' b m b'
+respond b  = liftF $ Respond b  id
+
+{- $compose
+    'Proxy' defines a 'Category', where the objects are the interfaces and the
+    morphisms are 'Proxy's parametrized on their initial input.
+
+    ('<-<') is composition and 'idT' is the identity.  The identity laws
+    guarantee that 'idT' is truly transparent:
+
+> idT <-< p = p
+>
+> p <-< idT = p
+
+    ... and the associativity law guarantees that 'Proxy' composition does not
+    depend on the grouping:
+
+> (p1 <-< p2) <-< p3 = p1 <-< (p2 <-< p3)
+
+    Note that in order to compose 'Proxy's, you must write them as functions of
+    their initial argument.  All subsequent arguments are bound by the 'respond'
+    command.  In other words, the actual composable unit is:
+
+> composable :: (Monad m) => b' -> Proxy a' a b' b m r
+-}
+
+infixr 9 <-<
+infixl 9 >->
+
+{-| Compose two proxies, satisfying all requests from downstream with responses
+    from upstream
+
+    Corresponds to ('.')/('<<<') from @Control.Category@ -}
+(<-<) :: (Monad m)
+ => (c' -> Proxy b' b c' c m r)
+ -> (b' -> Proxy a' a b' b m r)
+ -> (c' -> Proxy a' a c' c m r)
+p1 <-< p2 = \c' -> FreeT $ do
+    x1 <- runFreeT $ p1 c'
+    runFreeT $ case x1 of
+        Pure           r   -> return r
+        Free (Respond c  fc') -> wrap $ Respond c (fc' <-< p2)
+        Free (Request b' fb ) -> FreeT $ do
+            x2 <- runFreeT $ p2 b'
+            runFreeT $ case x2 of
+                Pure           r   -> return r
+                Free (Respond b  fb') -> ((\_ -> fb b) <-< fb') c'
+                Free (Request a' fa ) -> do
+                    let p1' = \_ -> FreeT $ return x1
+                    wrap $ Request a' $ \a -> (p1' <-< (\_ -> fa a)) c'
+
+{-| Compose two proxies, satisfying all requests from downstream with responses
+    from upstream
+
+    Corresponds to ('>>>') from @Control.Category@ -}
+(>->) :: (Monad m)
+ => (b' -> Proxy a' a b' b m r)
+ -> (c' -> Proxy b' b c' c m r)
+ -> (c' -> Proxy a' a c' c m r)
+(>->) = flip (<-<)
+
+{-| 'idT' acts like a \'T\'ransparent 'Proxy', passing all requests further
+    upstream, and passing all responses further downstream.
+
+    Corresponds to 'id' from @Control.Category@ -}
+idT :: (Monad m) => a' -> Proxy a' a a' a m r
+idT = \a' -> wrap $ Request a' $ \a -> wrap $ Respond a idT
+-- i.e. idT = foreverK $ request >=> respond
+
+{- $run
+    'runSession' ensures that the 'Proxy' passed to it does not have any
+    open responses or requests.  This restriction makes 'runSession' less
+    polymorphic than it could be, and I settled on this restriction for four
+    reasons:
+
+    * It prevents against accidental data loss.
+
+    * It protects against silent failures
+
+    * It prevents wastefully draining a scarce resource by gratuitously
+      driving it to completion
+
+    * It encourages an idiomatic programming style where unfulfilled requests
+      or responses are satisfied in a structured way using composition.
+
+    If you believe that loose requests or responses should be discarded or
+    ignored, then you can explicitly ignore them by using 'discard' (which
+    discards all responses), and 'ignore' (which ignores all requests):
+
+> runSession $ discard <-< p <-< ignore
+-}
+-- | Run a self-contained 'Session', converting it back to the base monad
+runSession :: (Monad m) => (() -> Session m r) -> m r
+runSession p = runSession' $ p ()
+
+runSession' p = do
+    x <- runFreeT p
+    case x of
+        Pure          r    -> return r
+        Free (Respond _ fb ) -> runSession' $ fb  ()
+        Free (Request _ fa') -> runSession' $ fa' ()
+
+{- $utility
+    'discard' provides a fallback 'Client' that gratuitously 'request's input
+    from a 'Server', but discards all responses.
+
+    'ignore' provides a fallback 'Server' that trivially 'respond's with output
+    to a 'Client', but ignores all request parameters.
+
+    Use 'foreverK' to abstract away the following common pattern:
+
+> p a = do
+>     ...
+>     a' <- respond b
+>     p a'
+
+    Using 'foreverK', you can avoid the manual recursion:
+
+> p = foreverK $ \a -> do
+>     ...
+>     respond b
+-}
+
+-- | Discard all responses
+discard :: (Monad m) => () -> Client () a m r
+discard () = forever $ request ()
+
+-- | Ignore all requests
+ignore  :: (Monad m) => a -> Server a () m r
+ignore  _  = forever $ respond ()
+
+-- | Compose a \'K\'leisli arrow with itself forever
+foreverK :: (Monad m) => (a -> m a) -> (a -> m b)
+foreverK k = let r = k >=> r in r
+{- foreverK uses 'let' to avoid a space leak.
+   See: http://hackage.haskell.org/trac/ghc/ticket/5205 -}
+
+{- $pipe
+    The following definitions are drop-in replacements for their 'Pipe'
+    equivalents.  Consult "Control.Pipe" and "Control.Pipe.Tutorial" for more
+    extensive documentation. -}
+
+{-| The type variables of @Pipe a b m r@ signify:
+
+    * @a@ - The type of input received from upstream pipes
+
+    * @b@ - The type of output delivered to downstream pipes
+
+    * @m@ - The base monad
+
+    * @r@ - The type of the return value -}
+type Pipe   a b = Proxy () a () b
+
+-- | A pipe that produces values
+type Producer b = Pipe ()    b
+
+-- | A pipe that consumes values
+type Consumer a = Pipe  a Void
+
+-- | A self-contained pipeline that is ready to be run
+type Pipeline   = Pipe () Void
+
+{-| Wait for input from upstream
+
+    'await' blocks until input is available -}
+await :: (Monad m) => Pipe a b m a
+await = request ()
+
+-- | Convert a pure function into a pipe
+pipe :: (Monad m) => (a -> b) -> Pipe a b m r
+pipe f = forever $ do
+    x <- await
+    yield (f x)
+
+{-| Deliver output downstream
+
+    'yield' restores control back downstream and binds the result to 'await'. -}
+yield :: (Monad m) => b -> Pipe a b m ()
+yield = respond
+
+infixr 9 <+<
+infixl 9 >+>
+
+-- | Corresponds to ('<<<')/('.') from @Control.Category@
+(<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r
+p1 <+< p2 = ((\() -> p1) <-< (\() -> p2)) ()
+
+-- | Corresponds to ('>>>') from @Control.Category@
+(>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
+(>+>) = flip (<+<)
+
+-- | Corresponds to 'id' from @Control.Category@
+idP :: (Monad m) => Pipe a a m r
+idP = idT ()
+
+-- | Run the 'Pipe' monad transformer, converting it back to the base monad
+runPipe :: (Monad m) => Pipeline m r -> m r
+runPipe p = do
+    x <- runFreeT p
+    case x of
+        Pure r -> return r
+        Free (Request _ f) -> runPipe (f ())
+        Free (Respond _ f) -> runPipe (f ())
diff --git a/Control/Proxy/Tutorial.hs b/Control/Proxy/Tutorial.hs
new file mode 100644
--- /dev/null
+++ b/Control/Proxy/Tutorial.hs
@@ -0,0 +1,492 @@
+-- | This module provides the tutorial for "Control.Proxy"
+
+module Control.Proxy.Tutorial (
+    -- * Basics
+    -- $basics
+
+    -- * Types
+    -- $types
+
+    -- * Composition
+    -- $composition
+
+    -- * Idioms
+    -- $idioms
+
+    -- * The importance of compositionality
+    -- $compose
+
+    -- * Mixing monads and composition
+    -- $monads
+
+    -- * Pipe Compatibility
+    -- $pipes
+    ) where
+
+import Control.Monad.Trans.Class
+import Control.Proxy
+
+{- $basics
+    The 'Proxy' type models composable chains of client-server interactions.
+
+    A 'Proxy' is a monad transformer that extends the base monad with the
+    ability to 'request' input from upstream and 'respond' with output to
+    downstream.
+
+    For example, consider the following toy remote procedure call
+    'Server':
+
+> import Control.Proxy
+> import Control.Monad.Trans
+>
+> incrementer :: Int -> Server Int Int IO r
+> incrementer question = do
+>     lift $ putStrLn $ "Server received : " ++ show question
+>     let answer = question + 1
+>     lift $ putStrLn $ "Server responded: " ++ show answer
+>     nextQuestion <- respond answer
+>     incrementer nextQuestion
+
+    We can understand what the 'Server' does just by looking at the type:
+
+>        | Question | Answer | Base monad | Return value
+> Server   Int        Int        IO           r
+
+    Our 'Server' receives questions about 'Int's, and responds with answers that
+    are 'Int's.  The base monad is 'IO' because our 'Server' 'lift's two
+    'putStrLn' statements to chat out loud.  The return value is polymorphic
+    because our 'Server' never terminates.
+
+    Note that the base monad doesn't always need to be 'IO'.  Unlike typical
+    servers, these kinds of 'Server's are pure syntax trees with no side
+    effects unless you call 'lift'.
+
+    Now we can write a 'Client' that interacts with our 'Server':
+
+> import Control.Monad
+>
+> oneTwoThree :: () -> Client Int Int IO ()
+> oneTwoThree () = forM_ [1, 2, 3] $ \question -> do
+>     lift $ putStrLn $ "Client requested: " ++ show question
+>     answer <- request question
+>     lift $ putStrLn $ "Client received : " ++ show answer
+>     lift $ putStrLn "*"
+
+    Again, the type explains what the 'Client' does:
+
+>        | Question | Answer | Base monad | Return value
+> Client   Int      | Int    | IO         | ()
+
+    Our 'Client' asks questions about 'Int's and receives answers that are
+    'Int's.  The 'Client' also uses 'IO' as the base monad.
+
+    We can then compose the 'Client' and 'Server' into a 'Session' using the
+    ('<-<') operator:
+
+> session :: () -> Session IO ()
+> session = oneTwoThree <-< incrementer
+
+    The 'Session' type indicates that we have a self-contained session that we
+    can run in the 'IO' monad.  We run it using the the 'runSession' function:
+
+>>> runSession session :: IO ()
+Client requested: 1
+Server received : 1
+Server responded: 2
+Client received : 2
+*
+Client requested: 2
+Server received : 2
+Server responded: 3
+Client received : 3
+*
+Client requested: 3
+Server received : 3
+Server responded: 4
+Client received : 4
+*
+
+    Now, let's add an intermediate 'Proxy' between the 'Client' and 'Server'
+    that subtly tampers with the stream going through it:
+
+> malicious :: Int -> Proxy Int Int Int Int IO r
+> malicious question = do
+>     question' <- if (question > 2)
+>                  then do
+>                      lift $ putStrLn "MUAHAHAHA!"
+>                      return (question + 1)
+>                  else return question
+>     answer <- request question'
+>     nextQuestion <- respond answer
+>     malicious nextQuestion
+
+    The type tells us what our 'Proxy' does:
+
+>       | Upstream (Server) | Downstream (Client) |
+>       | Question | Answer | Question |  Answer  | Base monad | Return value
+> Proxy   Int        Int      Int         Int       IO           r
+
+    A 'Proxy' bridges two separate interfaces.  The first two parameters define
+    the upstream interface (i.e. in the 'Server' direction) and the second two
+    parameters define the downstream interface (i.e. in the 'Client' direction).
+
+    We can see if our proxy does its job correctly:
+
+>>> runSession $ oneTwoThree <-< malicious <-< incrementer
+Client requested: 1
+Server received : 1
+Server responded: 2
+Client received : 2
+*
+Client requested: 2
+Server received : 2
+Server responded: 3
+Client received : 3
+*
+Client requested: 3
+MUAHAHAHA!
+Server received : 4
+Server responded: 5
+Client received : 5
+*
+
+    We can also add more proxies as we see fit:
+
+>>> runSession $ oneTwoThree <-< malicious <-< malicious <-< incrementer 
+Client requested: 1
+Server received : 1
+Server responded: 2
+Client received : 2
+*
+Client requested: 2
+Server received : 2
+Server responded: 3
+Client received : 3
+*
+Client requested: 3
+MUAHAHAHA!
+MUAHAHAHA!
+Server received : 5
+Server responded: 6
+Client received : 6
+*
+-}
+
+{- $types
+    You probably noticed something odd: ('<-<') seems to be composing values of
+    different types.  Sometimes it composes a 'Server' or a 'Client' or a
+    'Proxy'.  In reality, though, both 'Server' and 'Client' are just type
+    synonyms for special cases of 'Proxy':
+
+> type Server arg ret = Proxy Void  () arg  ret
+> type Client arg ret = Proxy  arg ret  () Void
+
+    A 'Server' is just a 'Proxy' that has no upstream interface, and a 'Client'
+    is just a 'Proxy' that has no downstream interface.  In fact, 'Session' is
+    a 'Proxy', too:
+
+> type Session        = Proxy Void  ()  () Void
+
+    A 'Session' is just a 'Proxy' that has neither an upstream interface nor a
+    downstream interface.
+
+    The 'Proxy' is the unifying type of the module that all other types derive
+    from and ('<-<') always composes two 'Proxy's and returns a new 'Proxy' of
+    the correct type.
+
+    You also probably noticed another odd thing: we parametrize every 'Proxy'
+    on its initial argument:
+
+>                +- Initial Arg
+>                |
+>                v
+> incrementer :: Int -> Server         Int Int IO r
+> malicious   :: Int -> Proxy  Int Int Int Int IO r
+> oneTwoThree :: ()  -> Client Int Int         IO ()
+>
+> session     :: ()  -> Session                IO ()
+
+    This input initializes each 'Proxy' and corresponds to the input on the
+    downstream interface.  I will expand the 'Server' and 'Client' type synonyms
+    to show this:
+
+>                +- Initial Arg = This -+
+>                |                      |
+>                v                      v
+> incrementer :: Int -> Proxy  Void ()  Int Int  IO r
+> malicious   :: Int -> Proxy  Int  Int Int Int  IO r
+> oneTwoThree :: ()  -> Proxy  Int  Int ()  Void IO ()
+>
+> session     :: ()  -> Proxy  Void ()  ()  Void IO ()
+
+    Composition supplies the first request through this initial parameters
+    and all subsequent requests are bound to 'respond' statements.
+
+    This means that the actual types you compose are all of the form:
+
+> proxy :: req_b -> Proxy req_a resp_a req_b resp_b m r
+-}
+
+{- $composition
+    'Proxy' composition posseses an identity 'Proxy' that is completely
+    transparent to anything upstream or downstream of it:
+
+> idT :: (Monad m) => req -> Proxy req resp req resp m r
+> idT question = do
+>     answer       <- request question
+>     nextQuestion <- respond answer
+>     idT nextQuestion
+
+    Transparency means that:
+
+> idT <-< p = p
+>
+> p <-< idT = p
+
+    Also, 'Proxy' composition has the nice property that it behaves exactly the
+    same way no matter how you group components:
+
+> (p1 <-< p2) <-< p3 = p1 <-< (p2 <-< p3)
+
+    This means that ('<-<') and 'idT' define a category, and the above equations
+    are the category laws.  These laws guarantee the following nice
+    properties of components:
+
+    * You can reason about each component's behavior independently of other
+      components
+
+    * You don't encounter boundary cases between components
+
+    * You don't encounter edge cases at the 'Server' or 'Client' ends
+
+    The semantics of 'Proxy' composition are simple:
+
+    * 'request' blocks until it receives a response from upstream
+
+    * 'respond' blocks until it receives a new request from downstream
+
+    * If any 'Proxy' in the chain terminates, the entire chain terminates
+-}
+
+{- $idioms
+    We frequently encounter the following recurring pattern when writing
+    'Proxy's:
+
+> someProxy arg = do
+>     ...
+>     nextArg <- respond x
+>     someProxy nexArg
+
+    "Control.Proxy" provides the 'foreverK' utility function which abstracts
+    away this manual recursion:
+
+> foreverK f = f >=> foreverK f
+
+    Using 'foreverK', we can simplify the definition of 'incrementer':
+
+> incrementer = foreverK $ \question -> do
+>     lift $ putStrLn $ "Server received : " ++ show question
+>     let answer = question + 1
+>     lift $ putStrLn $ "Server responded: " ++ show answer
+>     respond answer
+
+    ... which looks exactly like the way you might write server code in another
+    programming language.
+
+    We can similarly simplify 'malicious' this way:
+
+> malicious = foreverK $ \question -> do
+>     question' <- if (question > 2)
+>                  then do
+>                      lift $ putStrLn "MUAHAHAHA!"
+>                      return (question + 1)
+>                  else return question
+>     answer <- request question'
+>     respond answer
+
+    ... or 'idT':
+
+> idT = foreverK $ \question -> do
+>     answer <- request question
+>     respond answer
+>
+> -- or: idT = foreverK (request >=> respond)
+> --         = request >=> respond >=> request >=> respond >=> ...
+-}
+
+{- $compose
+    We can mix and match different components to rapidly define emergent
+    behaviors from a resuable set of core primitives.  For example, we could
+    replace our client with a command line prompt where the user requests
+    inputs:
+
+> inputPrompt :: (Read a, Show b) => () -> Client a b IO r
+> inputPrompt () = forever $ do
+>     str <- lift $ getLine
+>     let a = read str
+>     b <- request a
+>     lift $ print b
+>     lift $ putStrLn "*"
+
+>>> runSession $ inputPrompt <-< incrementer
+42<Enter>
+Server received : 42
+Server responded: 43
+43
+*
+666<Enter>
+Server received : 666
+Server responded: 667
+667
+*
+
+    Oh no, we lost our useful client diagnostic messages!  No worries, we can
+    abstract that functionality away into its own component:
+
+> diagnoseClient :: (Show a, Show b) => a -> Proxy a b a b IO r
+> diagnoseClient = foreverK $ \a -> do
+>     lift $ putStrLn $ "Client requested: " ++ show a
+>     b <- request a
+>     lift $ putStrLn $ "Client received : " ++ show b
+>     respond b
+
+>>> runSession $ inputPrompt <-< diagnoseClient <-< incrementer
+42<Enter>
+Client requested: 42
+Server received : 42
+Server responded: 43
+Client received : 43
+43
+*
+666<Enter>
+Client requested: 666
+Server received : 666
+Server responded: 667
+Client received : 667
+667
+*
+
+    Because of associativity, we can bundle @inputPrompt@ and @diagnoseClient@
+    into a single black box and not worry that the abstraction will leak due to
+    grouping issues:
+
+> verboseInput :: (Read a, Show b, Show a) => () -> Client a b IO r
+> verboseInput = inputPrompt <-< diagnoseClient
+
+>>> runSession $ verboseInput <-< incrementer
+<Exactly same behavior>
+
+    Or what if I want to cache the results coming out of @incrementer@?  I can
+    define a 'Proxy' to cache all requests going through it:
+
+> import qualified Data.Map as M
+>
+> cache :: (Ord a) => a -> Proxy a b a b IO r
+> cache = cache' M.empty
+>
+> cache' m a =
+>     case M.lookup a m of
+>         Nothing -> do
+>             b  <- request a
+>             a' <- respond b
+>             cache' (M.insert a b m) a'
+>         Just b  -> do
+>             lift $ putStrLn "Used cache!"
+>             a' <- respond b
+>             cache' m a'
+
+>>> runSession $ verboseInput <-< cache <-< incrementer 
+42<Enter>
+Client requested: 42
+Server received : 42
+Server responded: 43
+Client received : 43
+43
+*
+42<Enter>
+Client requested: 42
+Used cache!
+Client received : 43
+43
+*
+
+    Note that I don't distinguish between a "reverse proxy" or a "forward proxy"
+    since composition doesn't distinguish either.  You can attach the @cache@
+    'Proxy' to a 'Client':
+
+> client' = client <-< cache
+
+    ... or to a 'Server':
+
+> server' = cache <-< server
+
+    ... or anywhere in between.  It's completely up to you!
+-}
+
+{- $monads
+    All the previous examples use a single composition chain, but you need not
+    restrict yourself to that design pattern.  Remember that the result of
+    composition is a 'Proxy' itself (parametrized by an input), and 'Proxy's are
+    'Monad's, so you can bind the result of composition directly within another
+    @do@ block to generate complex behaviors:
+
+> mixedClient :: () -> Client Int Int IO r
+> mixedClient () = do
+>     oneTwoThree ()
+>     -- Here we bind composition within a larger do block
+>     (inputPrompt <-< cache) ()
+
+>>> runSession $ mixedClient <-< incrementer
+Client requested: 1
+Server received : 1
+Server responded: 2
+Client received : 2
+*
+Client requested: 2
+Server received : 2
+Server responded: 3
+Client received : 3
+*
+Client requested: 3
+Server received : 3
+Server responded: 4
+Client received : 4
+*
+42<Enter>
+Server received : 42
+Server responded: 43
+43
+*
+42<Enter>
+Used cache!
+43
+*
+
+    So feel free to use your imagination!  Up until the moment you call
+    'runSession', you can freely mix composition or @do@ notation within each
+    other.
+-}
+
+{- $pipes
+    'Proxy's generalize 'Pipe's by permitting communication upstream.
+    Fortunately, though, you don't need to rewrite your code if you have already
+    used 'Pipe's.  "Control.Proxy" formulates all of the 'Pipe' types and
+    primitives in terms of the 'Proxy' type.
+
+    This means that if you wish to upgrade your 'Pipe' code to take advantage of
+    upstream communication, you only need to import "Control.Proxy" instead
+    of "Control.Pipe" and everything will still work out of the box.  Then you
+    can selectively upgrade certain components to communicate upstream as
+    necessary.
+
+    To understand how 'Pipe's map onto 'Proxy's, just check out the 'Pipe'
+    definition in "Control.Proxy":
+
+> type Pipe a b = Proxy () a () b
+
+    In other words, a 'Pipe' is just a 'Proxy' where you never pass any
+    informationupstream.
+
+    "Control.Pipe" will not be deprecated, however, and will be preserved for
+    users who do not wish to communicate information upstream.
+-}
diff --git a/pipes.cabal b/pipes.cabal
--- a/pipes.cabal
+++ b/pipes.cabal
@@ -1,5 +1,5 @@
 Name: pipes
-Version: 2.2.0
+Version: 2.3.0
 Cabal-Version: >=1.14.0
 Build-Type: Simple
 License: BSD3
@@ -39,8 +39,13 @@
     Vertical Concatenation always works the way you expect, picking up where the
     previous 'Pipe' left off.
   .
+  * /Bidirectionality/: The library now provides a bidirectional 'Pipe' type,
+    called a 'Proxy'.
+  .
   Check out "Control.Pipe.Tutorial" for a copious introductory tutorial and
-  "Control.Pipe" for the actual implementation.
+  "Control.Pipe" for the actual implementation.  "Control.Proxy.Tutorial"
+  introduces bidirectional iteratees that are backwards-compatible with 'Pipe's
+  and "Control.Proxy" provides the implementation.
 Category: Control, Enumerator
 Tested-With: GHC ==7.4.1
 Source-Repository head
@@ -58,6 +63,8 @@
         Control.Frame,
         Control.Frame.Tutorial,
         Control.IMonad.Trans.Free,
+        Control.Proxy,
+        Control.Proxy.Tutorial,
         Control.Pipe,
         Control.Pipe.Tutorial
     GHC-Options: -O2
