Safe Haskell | Safe |
---|---|
Language | Haskell98 |
The Proxy
class defines the library's core API. Everything else in this
library builds exclusively on top of the Proxy
type class so that all
proxy implementations and extensions can share the same standard library.
Several of these type classes duplicate methods from familiar type-classes
(such as (?>=
) duplicating (>>=
)). You do NOT need to use these
duplicate methods. Instead, read the "Polymorphic proxies" section below
which explains their purpose and how they help clean up type signatures.
- class Proxy p where
- idT :: (Monad m, Proxy p) => a' -> p a' a a' a m r
- coidT :: (Monad m, Proxy p) => a -> p a' a a' a m r
- (<-<) :: (Monad m, Proxy p) => (c' -> p b' b c' c m r) -> (b' -> p a' a b' b m r) -> c' -> p a' a c' c m r
- (<~<) :: (Monad m, Proxy p) => (b -> p b' b c' c m r) -> (a -> p a' a b' b m r) -> a -> p a' a c' c m r
- class Interact p where
- (/</) :: (Monad m, Interact p) => (c' -> p b' b x' x m c) -> (b' -> p a' a x' x m b) -> c' -> p a' a x' x m c
- (\<\) :: (Monad m, Interact p) => (b -> p x' x c' c m b') -> (a -> p x' x b' b m a') -> a -> p x' x c' c m a'
- class Proxy p => MonadPlusP p where
- class Proxy p => MonadIOP p where
Core proxy class
The core API for the pipes
library
You should only use request
, respond
, and (>->
)
I only provide (>~>
) for theoretical symmetry, and the remaining methods
just implement internal type class plumbing.
request :: Monad m => a' -> p a' a b' b m a Source #
request
input from upstream, passing an argument with the request
request a'
passes a'
as a parameter to upstream that upstream may
use to decide what response to return. request
binds the upstream's
response of type a
to its own return value.
respond :: Monad m => b -> p a' a b' b m b' Source #
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 of type b'
from the next request
as its return value.
(>->) :: Monad m => (b' -> p a' a b' b m r) -> (c' -> p b' b c' c m r) -> c' -> p a' a c' c m r infixl 7 Source #
Compose two proxies blocked on a respond
, generating a new proxy
blocked on a respond
Begins from the downstream end and satisfies every request
with a
respond
(>~>) :: Monad m => (a -> p a' a b' b m r) -> (b -> p b' b c' c m r) -> a -> p a' a c' c m r Source #
Compose two proxies blocked on a request
, generating a new proxy
blocked on a request
Begins from the upstream end and satisfies every respond
with a
request
return_P :: Monad m => r -> p a' a b' b m r Source #
(?>=) :: Monad m => p a' a b' b m r -> (r -> p a' a b' b m r') -> p a' a b' b m r' infixl 1 Source #
lift_P :: Monad m => m r -> p a' a b' b m r Source #
hoist_P :: Monad m => (forall r. m r -> n r) -> p a' a b' b m r' -> p a' a b' b n r' Source #
idT :: (Monad m, Proxy p) => a' -> p a' a a' a m r Source #
idT
forwards requests followed by responses
idT = request >=> respond >=> idT
coidT :: (Monad m, Proxy p) => a -> p a' a a' a m r Source #
coidT
forwards responses followed by requests
coidT = respond >=> request >=> coidT
(<-<) :: (Monad m, Proxy p) => (c' -> p b' b c' c m r) -> (b' -> p a' a b' b m r) -> c' -> p a' a c' c m r infixr 7 Source #
(<~<) :: (Monad m, Proxy p) => (b -> p b' b c' c m r) -> (a -> p a' a b' b m r) -> a -> p a' a c' c m r Source #
request/respond substitution
class Interact p where Source #
Two extra Proxy categories of theoretical interest
(\>\) :: Monad m => (b' -> p a' a x' x m b) -> (c' -> p b' b x' x m c) -> c' -> p a' a x' x m c infixl 8 Source #
f \>\ g
replaces all request
s in g
with f
.
(/>/) :: Monad m => (a -> p x' x b' b m a') -> (b -> p x' x c' c m b') -> a -> p x' x c' c m a' infixr 8 Source #
f />/ g
replaces all respond
s in f
with g
.
(/</) :: (Monad m, Interact p) => (c' -> p b' b x' x m c) -> (b' -> p a' a x' x m b) -> c' -> p a' a x' x m c infixr 8 Source #
f /</ g
replaces all request
s in f
with g
.
(\<\) :: (Monad m, Interact p) => (b -> p x' x c' c m b') -> (a -> p x' x b' b m a') -> a -> p x' x c' c m a' infixl 8 Source #
f \<\ g
replaces all respond
s in g
with f
.
Laws
The Proxy
class defines an interface to all core proxy capabilities that
all proxy-like types must implement.
First, all proxies must support a bidirectional flow of information, defined by:
Intuitively, both p1 >-> p2
and p1 >~> p2
pair each request
in p2
with a respond
in p1
. (>->
) accepts proxies blocked on respond
and
begins from the downstream end, whereas (>~>
) accepts proxies blocked on
request
and begins from the upstream end.
Second, all proxies are monads, defined by:
These must satify the monad laws using (>>=) = (?>=)
and
return = return_P
.
Third, all proxies are monad transformers, defined by:
This must satisfy the monad transformer laws, using lift = lift_P
.
Fourth, all proxies are functors in the category of monads, defined by:
This must satisfy the functor laws, using hoist = hoist_P
.
All Proxy
instances must satisfy these additional laws:
Define: idT = request >=> respond >=> idT idT >-> p = p p >-> idT = p (p1 >-> p2) >-> p3 = p1 >-> (p2 >-> p3)
Define: coidT = respond >=> request >=> coidT coidT >~> p = p p >~> coidT = p (p1 >~> p2) >~> p3 = p1 >~> (p2 >~> p3)
Also, all proxies must satisfy the following Proxy
laws:
-- Define: liftK = (lift .) p1 >-> liftK f = liftK f p1 >-> (liftK f >=> respond >=> p2) = liftK f >=> respond >=> (p1 >-> p2) (liftK g >=> respond >=> p1) >-> (liftK f >=> request >=> liftK h >=> p2) = liftK (f >=> g >=> h) >=> (p1 >-> p2) (liftK g >=> request >=> p1) >-> (liftK f >=> request >=> p2) = liftK (f >=> g) >=> request >=> (p1 >~> p2) liftK f >~> p2 = liftK f (liftK f >=> request >=> p1) >~> p2 = liftK f >=> request >=> (p1 >~> p2) (liftK f >=> respond >=> liftK h >=> p1) >~> (liftK g >=> request >=> p2) = liftK (f >=> g >=> h) >=> (p1 >~> p2) (liftK f >=> respond >=> p1) >~> (liftK g >=> respond >=> p2) = liftK (f >=> g) >=> (p1 >-> p2)
The Interact
class exists primarily for theoretical interest and to
justify some of the functor laws for the ProxyTrans
type class. You will
probably never use it.
The Interact
class defines the ability to:
Laws:
request \>\ f = f f \>\ request = f (f \>\ g) \>\ h = f \>\ (g \>\ h)
respond />/ f = f f />/ respond = f (f />/ g) />/ h = f />/ (g />/ h)
Additionally, (\>\
) and (/>/
) distribute in one direction over Kleisli
composition:
a \>\ (b >=> c) = (a \>\ b) >=> (a \>\ c) a \>\ return = return
(b >=> c) />/ a = (b />/ a) >=> (c />/ a) return />/ a = return
Polymorphic proxies
Many of these type classes contain methods which copy methods from more familiar type classes. These duplicate methods serve two purposes.
First, this library requires type class instances that would otherwise be impossible to define without providing higher-kinded constraints. Rather than use the following illegal polymorphic constraint:
instance (forall a' a b' b . MonadTrans (p a' a b' b)) => ...
... the instance can instead use the following Haskell98 constraint:
instance (MonadTransP p) => ...
Second, these type classes don't require the FlexibleContexts
extension
to use and substantially clean up constraints in type signatures. They
convert messy constraints like this:
p :: (MonadP (p a' a b' b m), MonadTrans (p a' a b' b)) => ...
.. into cleaner and more general constraints like this:
P :: (Proxy p) => ...
These type classes exist solely for internal plumbing and you should never
directly use the duplicate methods from them. Instead, you can use all the
original type classes as long as you embed your proxy code within at least
one proxy transformer (or IdentityP
if don't use any transformers). The
type-class machinery will then automatically convert the messier and less
polymorphic constraints to the simpler and more general constraints.
For example, consider the following almost-correct definition for mapMD
(from Control.Proxy.Prelude.Base):
import Control.Monad.Trans.Class import Control.Proxy mapMD f = foreverK $ \a' -> do a <- request a' b <- lift (f a) respond b
The compiler infers the following messy constraint:
mapMD :: (Monad m, Monad (p x a x b m), MonadTrans (p x a x b), Proxy p) => (a -> m b) -> x -> p x a x b m r
Instead, you can embed the code in the IdentityP
proxy transformer by
wrapping it in runIdentityK
:
-- |difference| mapMD f = runIdentityK $ foreverK $ \a' -> do a <- request a' b <- lift (f a) respond b
... and now the compiler collapses all the constraints into the Proxy
constraint:
mapMD :: (Monad m, Proxy p) => (a -> m b) -> x -> p x a x b m r
You do not incur any performance penalty for writing polymorphic code or
embedding it in IdentityP
. This library employs several rewrite RULES
which transform your polymorphic code into the equivalent type-specialized
hand-tuned code. These rewrite rules fire very robustly and they do not
require any assistance on your part from compiler pragmas like INLINE
,
NOINLINE
or SPECIALIZE
.
If you nest proxies within proxies:
example () = do request () lift $ request () lift $ lift $ request ()
... then you can still keep the nice constraints using:
example () = runIdentityP . hoist (runIdentityP . hoist runIdentityP) $ do request () lift $ request () lift $ lift $ request ()
You don't need to use runIdentityP
/ runIdentityK
if you use any other
proxy transformers (In fact you can't, it's a type error). The following
code example illustrates this, where the throw
command (from the EitherP
proxy transformer) suffices to guide the compiler to the cleaner type
signature:
import Control.Monad import Control.Proxy import qualified Control.Proxy.Trans.Either as E example :: (Monad m, Proxy p) => () -> Producer (EitherP String p) Char m () example () = do c <- request () when (c == ' ') $ E.throw "Error: received space" respond c
class Proxy p => MonadPlusP p where Source #
The (MonadPlusP p)
constraint is equivalent to the following constraint:
(forall a' a b' b m . (Monad m) => MonadPlus (p a' a b' b m)) => ...
mzero_P :: Monad m => p a' a b' b m r Source #
mplus_P :: Monad m => p a' a b' b m r -> p a' a b' b m r -> p a' a b' b m r Source #
MonadPlusP p => MonadPlusP (IdentityP p) Source # | |
Proxy p => MonadPlusP (MaybeP p) Source # | |
MonadPlusP p => MonadPlusP (EitherP e p) Source # | |
MonadPlusP p => MonadPlusP (ReaderP i p) Source # | |
MonadPlusP p => MonadPlusP (StateP s p) Source # | |
MonadPlusP p => MonadPlusP (WriterP w p) Source # | |
class Proxy p => MonadIOP p where Source #
The (MonadIOP p)
constraint is equivalent to the following constraint:
(forall a' a b' b m . (MonadIO m) => MonadIO (p a' a b' b m)) => ...
MonadIOP ProxyFast Source # | |
MonadIOP ProxyCorrect Source # | |
MonadIOP p => MonadIOP (IdentityP p) Source # | |
MonadIOP p => MonadIOP (MaybeP p) Source # | |
MonadIOP p => MonadIOP (EitherP e p) Source # | |
MonadIOP p => MonadIOP (ReaderP i p) Source # | |
MonadIOP p => MonadIOP (StateP s p) Source # | |
MonadIOP p => MonadIOP (WriterP w p) Source # | |