pipes-3.1.0: Compositional pipelines

Safe HaskellSafe
LanguageHaskell98

Control.Proxy.Class

Contents

Description

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.

Synopsis

Core proxy class

class Proxy p where Source #

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.

Minimal complete definition

request, respond, (>->), (>~>), return_P, (?>=), lift_P, hoist_P

Methods

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 requests 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 #

return_P is identical to return, except with a more polymorphic constraint.

(?>=) :: 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 #

(?>=) is identical to (>>=), except with a more polymorphic constraint.

lift_P :: Monad m => m r -> p a' a b' b m r Source #

lift_P is identical to lift, except with a more polymorphic constraint.

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 #

hoist_P is identical to hoist, except with a more polymorphic constraint.

Instances

Proxy ProxyFast Source # 

Methods

request :: Monad m => a' -> ProxyFast a' a b' b m a Source #

respond :: Monad m => b -> ProxyFast a' a b' b m b' Source #

(>->) :: Monad m => (b' -> ProxyFast a' a b' b m r) -> (c' -> ProxyFast b' b c' c m r) -> c' -> ProxyFast a' a c' c m r Source #

(>~>) :: Monad m => (a -> ProxyFast a' a b' b m r) -> (b -> ProxyFast b' b c' c m r) -> a -> ProxyFast a' a c' c m r Source #

return_P :: Monad m => r -> ProxyFast a' a b' b m r Source #

(?>=) :: Monad m => ProxyFast a' a b' b m r -> (r -> ProxyFast a' a b' b m r') -> ProxyFast a' a b' b m r' Source #

lift_P :: Monad m => m r -> ProxyFast a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> ProxyFast a' a b' b m r' -> ProxyFast a' a b' b n r' Source #

Proxy ProxyCorrect Source # 

Methods

request :: Monad m => a' -> ProxyCorrect a' a b' b m a Source #

respond :: Monad m => b -> ProxyCorrect a' a b' b m b' Source #

(>->) :: Monad m => (b' -> ProxyCorrect a' a b' b m r) -> (c' -> ProxyCorrect b' b c' c m r) -> c' -> ProxyCorrect a' a c' c m r Source #

(>~>) :: Monad m => (a -> ProxyCorrect a' a b' b m r) -> (b -> ProxyCorrect b' b c' c m r) -> a -> ProxyCorrect a' a c' c m r Source #

return_P :: Monad m => r -> ProxyCorrect a' a b' b m r Source #

(?>=) :: Monad m => ProxyCorrect a' a b' b m r -> (r -> ProxyCorrect a' a b' b m r') -> ProxyCorrect a' a b' b m r' Source #

lift_P :: Monad m => m r -> ProxyCorrect a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> ProxyCorrect a' a b' b m r' -> ProxyCorrect a' a b' b n r' Source #

Proxy p => Proxy (IdentityP p) Source # 

Methods

request :: Monad m => a' -> IdentityP p a' a b' b m a Source #

respond :: Monad m => b -> IdentityP p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> IdentityP p a' a b' b m r) -> (c' -> IdentityP p b' b c' c m r) -> c' -> IdentityP p a' a c' c m r Source #

(>~>) :: Monad m => (a -> IdentityP p a' a b' b m r) -> (b -> IdentityP p b' b c' c m r) -> a -> IdentityP p a' a c' c m r Source #

return_P :: Monad m => r -> IdentityP p a' a b' b m r Source #

(?>=) :: Monad m => IdentityP p a' a b' b m r -> (r -> IdentityP p a' a b' b m r') -> IdentityP p a' a b' b m r' Source #

lift_P :: Monad m => m r -> IdentityP p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> IdentityP p a' a b' b m r' -> IdentityP p a' a b' b n r' Source #

Proxy p => Proxy (MaybeP p) Source # 

Methods

request :: Monad m => a' -> MaybeP p a' a b' b m a Source #

respond :: Monad m => b -> MaybeP p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> MaybeP p a' a b' b m r) -> (c' -> MaybeP p b' b c' c m r) -> c' -> MaybeP p a' a c' c m r Source #

(>~>) :: Monad m => (a -> MaybeP p a' a b' b m r) -> (b -> MaybeP p b' b c' c m r) -> a -> MaybeP p a' a c' c m r Source #

return_P :: Monad m => r -> MaybeP p a' a b' b m r Source #

(?>=) :: Monad m => MaybeP p a' a b' b m r -> (r -> MaybeP p a' a b' b m r') -> MaybeP p a' a b' b m r' Source #

lift_P :: Monad m => m r -> MaybeP p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> MaybeP p a' a b' b m r' -> MaybeP p a' a b' b n r' Source #

Proxy p => Proxy (EitherP e p) Source # 

Methods

request :: Monad m => a' -> EitherP e p a' a b' b m a Source #

respond :: Monad m => b -> EitherP e p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> EitherP e p a' a b' b m r) -> (c' -> EitherP e p b' b c' c m r) -> c' -> EitherP e p a' a c' c m r Source #

(>~>) :: Monad m => (a -> EitherP e p a' a b' b m r) -> (b -> EitherP e p b' b c' c m r) -> a -> EitherP e p a' a c' c m r Source #

return_P :: Monad m => r -> EitherP e p a' a b' b m r Source #

(?>=) :: Monad m => EitherP e p a' a b' b m r -> (r -> EitherP e p a' a b' b m r') -> EitherP e p a' a b' b m r' Source #

lift_P :: Monad m => m r -> EitherP e p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> EitherP e p a' a b' b m r' -> EitherP e p a' a b' b n r' Source #

Proxy p => Proxy (ReaderP i p) Source # 

Methods

request :: Monad m => a' -> ReaderP i p a' a b' b m a Source #

respond :: Monad m => b -> ReaderP i p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> ReaderP i p a' a b' b m r) -> (c' -> ReaderP i p b' b c' c m r) -> c' -> ReaderP i p a' a c' c m r Source #

(>~>) :: Monad m => (a -> ReaderP i p a' a b' b m r) -> (b -> ReaderP i p b' b c' c m r) -> a -> ReaderP i p a' a c' c m r Source #

return_P :: Monad m => r -> ReaderP i p a' a b' b m r Source #

(?>=) :: Monad m => ReaderP i p a' a b' b m r -> (r -> ReaderP i p a' a b' b m r') -> ReaderP i p a' a b' b m r' Source #

lift_P :: Monad m => m r -> ReaderP i p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> ReaderP i p a' a b' b m r' -> ReaderP i p a' a b' b n r' Source #

Proxy p => Proxy (StateP s p) Source # 

Methods

request :: Monad m => a' -> StateP s p a' a b' b m a Source #

respond :: Monad m => b -> StateP s p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> StateP s p a' a b' b m r) -> (c' -> StateP s p b' b c' c m r) -> c' -> StateP s p a' a c' c m r Source #

(>~>) :: Monad m => (a -> StateP s p a' a b' b m r) -> (b -> StateP s p b' b c' c m r) -> a -> StateP s p a' a c' c m r Source #

return_P :: Monad m => r -> StateP s p a' a b' b m r Source #

(?>=) :: Monad m => StateP s p a' a b' b m r -> (r -> StateP s p a' a b' b m r') -> StateP s p a' a b' b m r' Source #

lift_P :: Monad m => m r -> StateP s p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> StateP s p a' a b' b m r' -> StateP s p a' a b' b n r' Source #

Proxy p => Proxy (WriterP w p) Source # 

Methods

request :: Monad m => a' -> WriterP w p a' a b' b m a Source #

respond :: Monad m => b -> WriterP w p a' a b' b m b' Source #

(>->) :: Monad m => (b' -> WriterP w p a' a b' b m r) -> (c' -> WriterP w p b' b c' c m r) -> c' -> WriterP w p a' a c' c m r Source #

(>~>) :: Monad m => (a -> WriterP w p a' a b' b m r) -> (b -> WriterP w p b' b c' c m r) -> a -> WriterP w p a' a c' c m r Source #

return_P :: Monad m => r -> WriterP w p a' a b' b m r Source #

(?>=) :: Monad m => WriterP w p a' a b' b m r -> (r -> WriterP w p a' a b' b m r') -> WriterP w p a' a b' b m r' Source #

lift_P :: Monad m => m r -> WriterP w p a' a b' b m r Source #

hoist_P :: Monad m => (forall r. m r -> n r) -> WriterP w p a' a b' b m r' -> WriterP w 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 #

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, 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 #

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

You don't need to use this. I include it only for symmetry.

request/respond substitution

class Interact p where Source #

Two extra Proxy categories of theoretical interest

Minimal complete definition

(\>\), (/>/)

Methods

(\>\) :: 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 requests 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 responds in f with g.

Instances

Interact ProxyFast Source # 

Methods

(\>\) :: Monad m => (b' -> ProxyFast a' a x' x m b) -> (c' -> ProxyFast b' b x' x m c) -> c' -> ProxyFast a' a x' x m c Source #

(/>/) :: Monad m => (a -> ProxyFast x' x b' b m a') -> (b -> ProxyFast x' x c' c m b') -> a -> ProxyFast x' x c' c m a' Source #

Interact ProxyCorrect Source # 

Methods

(\>\) :: Monad m => (b' -> ProxyCorrect a' a x' x m b) -> (c' -> ProxyCorrect b' b x' x m c) -> c' -> ProxyCorrect a' a x' x m c Source #

(/>/) :: Monad m => (a -> ProxyCorrect x' x b' b m a') -> (b -> ProxyCorrect x' x c' c m b') -> a -> ProxyCorrect x' x c' c m a' Source #

Interact p => Interact (IdentityP p) Source # 

Methods

(\>\) :: Monad m => (b' -> IdentityP p a' a x' x m b) -> (c' -> IdentityP p b' b x' x m c) -> c' -> IdentityP p a' a x' x m c Source #

(/>/) :: Monad m => (a -> IdentityP p x' x b' b m a') -> (b -> IdentityP p x' x c' c m b') -> a -> IdentityP p x' x c' c m a' Source #

Interact p => Interact (ReaderP i p) Source # 

Methods

(\>\) :: Monad m => (b' -> ReaderP i p a' a x' x m b) -> (c' -> ReaderP i p b' b x' x m c) -> c' -> ReaderP i p a' a x' x m c Source #

(/>/) :: Monad m => (a -> ReaderP i p x' x b' b m a') -> (b -> ReaderP i p x' x c' c m b') -> a -> ReaderP i p x' x c' c m a' Source #

(/</) :: (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 requests 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 responds 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:

  • (>->) and idT form a category:
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)) => ...

Minimal complete definition

mzero_P, mplus_P

Methods

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 #

Instances

MonadPlusP p => MonadPlusP (IdentityP p) Source # 

Methods

mzero_P :: Monad m => IdentityP p a' a b' b m r Source #

mplus_P :: Monad m => IdentityP p a' a b' b m r -> IdentityP p a' a b' b m r -> IdentityP p a' a b' b m r Source #

Proxy p => MonadPlusP (MaybeP p) Source # 

Methods

mzero_P :: Monad m => MaybeP p a' a b' b m r Source #

mplus_P :: Monad m => MaybeP p a' a b' b m r -> MaybeP p a' a b' b m r -> MaybeP p a' a b' b m r Source #

MonadPlusP p => MonadPlusP (EitherP e p) Source # 

Methods

mzero_P :: Monad m => EitherP e p a' a b' b m r Source #

mplus_P :: Monad m => EitherP e p a' a b' b m r -> EitherP e p a' a b' b m r -> EitherP e p a' a b' b m r Source #

MonadPlusP p => MonadPlusP (ReaderP i p) Source # 

Methods

mzero_P :: Monad m => ReaderP i p a' a b' b m r Source #

mplus_P :: Monad m => ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r Source #

MonadPlusP p => MonadPlusP (StateP s p) Source # 

Methods

mzero_P :: Monad m => StateP s p a' a b' b m r Source #

mplus_P :: Monad m => StateP s p a' a b' b m r -> StateP s p a' a b' b m r -> StateP s p a' a b' b m r Source #

MonadPlusP p => MonadPlusP (WriterP w p) Source # 

Methods

mzero_P :: Monad m => WriterP w p a' a b' b m r Source #

mplus_P :: Monad m => WriterP w p a' a b' b m r -> WriterP w p a' a b' b m r -> WriterP w p a' a b' b m r 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)) => ...

Minimal complete definition

liftIO_P

Methods

liftIO_P :: MonadIO m => IO r -> p a' a b' b m r Source #

Instances

MonadIOP ProxyFast Source # 

Methods

liftIO_P :: MonadIO m => IO r -> ProxyFast a' a b' b m r Source #

MonadIOP ProxyCorrect Source # 

Methods

liftIO_P :: MonadIO m => IO r -> ProxyCorrect a' a b' b m r Source #

MonadIOP p => MonadIOP (IdentityP p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> IdentityP p a' a b' b m r Source #

MonadIOP p => MonadIOP (MaybeP p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> MaybeP p a' a b' b m r Source #

MonadIOP p => MonadIOP (EitherP e p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> EitherP e p a' a b' b m r Source #

MonadIOP p => MonadIOP (ReaderP i p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> ReaderP i p a' a b' b m r Source #

MonadIOP p => MonadIOP (StateP s p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> StateP s p a' a b' b m r Source #

MonadIOP p => MonadIOP (WriterP w p) Source # 

Methods

liftIO_P :: MonadIO m => IO r -> WriterP w p a' a b' b m r Source #