Safe Haskell | Safe |
---|---|
Language | Haskell98 |
This module provides an API similar to Control.Pipe for those who prefer
the classic Pipe
API.
This module differs slightly from Control.Pipe in order to promote seamless interoperability with both pipes and proxies. See the "Upgrade Pipes to Proxies" section below for details.
- await :: (Monad m, Proxy p) => Pipe p a b m a
- yield :: (Monad m, Proxy p) => b -> p a' a () b m ()
- pipe :: (Monad m, Proxy p) => (a -> b) -> Pipe p a b m r
- (<+<) :: (Monad m, Proxy p) => Pipe p b c m r -> Pipe p a b m r -> Pipe p a c m r
- (>+>) :: (Monad m, Proxy p) => Pipe p a b m r -> Pipe p b c m r -> Pipe p a c m r
- idP :: (Monad m, Proxy p) => Pipe p a a m r
- type Pipeline p = p C () () C
Create Pipes
await :: (Monad m, Proxy p) => Pipe p a b m a Source #
Wait for input from upstream
await
blocks until input is available from upstream.
pipe :: (Monad m, Proxy p) => (a -> b) -> Pipe p a b m r Source #
Convert a pure function into a pipe
Compose Pipes
(<+<) :: (Monad m, Proxy p) => Pipe p b c m r -> Pipe p a b m r -> Pipe p a c m r infixr 9 Source #
Corresponds to (<<<
)/(.
) from Control.Category
(>+>) :: (Monad m, Proxy p) => Pipe p a b m r -> Pipe p b c m r -> Pipe p a c m r infixl 9 Source #
Corresponds to (>>>
) from Control.Category
Synonyms
Run Pipes
The Control.Proxy.Core.Fast and Control.Proxy.Core.Correct modules
provide their corresponding runPipe
functions, specialized to their own
Proxy
implementations.
Each implementation must supply its own runPipe
function since it is
the only non-polymorphic Pipe
function and the compiler uses it to
select which underlying proxy implementation to use.
Upgrade Pipes to Proxies
You can upgrade classic Pipe
code to work with the proxy ecosystem in
steps. Each change enables greater interoperability with proxy utilities
and transformers and if time permits you should implement the entire upgrade
for your libraries if you want to take advantage of proxy standard
libraries.
First, import Control.Proxy and Control.Proxy.Pipe instead of
Control.Pipe. Then, add ProxyFast
after every Pipe
, Producer
, or
Consumer
in any type signature. For example, you would convert this:
import Control.Pipe fromList :: (Monad m) => [b] -> Producer b m () fromList xs = mapM_ yield xs
... to this:
import Control.Proxy import Control.Proxy.Pipe -- transition import fromList :: (Monad m) => [b] -> Producer ProxyFast b m () fromList xs = mapM_ yield xs
The change ensures that all your code now works in the ProxyFast
monad,
which is the faster of the two proxy implementations.
Second, modify all your Pipe
s to take an empty '()' as their final
argument, and translate the following functions:
- (
<+<
) to (<-<
) runPipe
torunProxy
For example, you would convert this:
import Control.Proxy import Control.Proxy.Pipe fromList :: (Monad m) => [b] -> Producer ProxyFast b m () fromList xs = mapM_ yield xs
... to this:
import Control.Proxy import Control.Proxy.Pipe fromList :: (Monad m) => [b] -> () -> Producer ProxyFast b m () fromList xs () = mapM_ yield xs
Now when you call these within a do
block you must supplying an
additional ()
argument:
examplePipe () = do a <- request () fromList [1..a] ()
This change lets you switch from pipe composition, (<+<
), to proxy
composition, (<-<
), so that you can mix proxy utilities with pipes.
Third, wrap your pipe's implementation in runIdentityP
(which
Control.Proxy exports):
import Control.Proxy import Control.Proxy.Pipe fromList xs () = runIdentityP $ mapM_ yield xs
Then replace the ProxyFast
in the type signature with a type variable p
constrained by the Proxy
type class:
fromList :: (Monad m, Proxy p) => [b] -> () -> Producer p b m ()
This change upgrades your Pipe
to work natively within proxies and proxy
transformers, without any manual conversion or lifting. You can now compose
or sequence your Pipe
within any feature set transparently.
Finally, replace each await
with request ()
and each yield
with
respond
. Also, replace every Pipeline
with Session
. This lets you
drop the Control.Proxy.Pipe import:
import Control.Proxy fromList :: (Monad m, Proxy p) => [b] -> () -> Producer p b m () fromList xs () = runIdentityP $ mapM_ respond xs
Also, I encourage you to continue using the Pipe
, Consumer
and
Producer
type synonyms to simplify type signatures. The following
examples show how they cleanly mix with proxies and their extensions:
import Control.Proxy import Control.Proxy.Trans.Either as E import Control.Proxy.Trans.State -- A Producer enriched with pipe-local state example1 :: (Monad m, Proxy p) => () -> Producer (StateP Int p) Int m r example1 () = forever $ do n <- get respond n put (n + 1) -- A Consumer enriched with error-handling example2 :: (Proxy p) => () -> Consumer (EitherP String p) Int IO () example2 () = do n <- request () if (n == 0) then E.throw "Error: received 0" else lift $ print n