packages feed

pipes 2.2.0 → 2.3.0

raw patch · 4 files changed

+845/−14 lines, 4 files

Files

Control/Pipe.hs view
@@ -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
+ Control/Proxy.hs view
@@ -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 ())
+ Control/Proxy/Tutorial.hs view
@@ -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.+-}
pipes.cabal view
@@ -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