pipes-3.3.0: Compositional pipelines

Safe HaskellSafe-Inferred

Control.Proxy.Class

Contents

Description

This module defines the theoretical framework underpinning this library

Synopsis

The Proxy Class

class ProxyInternal p => Proxy p whereSource

The Proxy class defines a Monad that intersects four streaming categories:

This class requires the "point-ful" version of each category's composition operator for efficiency.

Minimal definition:

Methods

request :: Monad m => a' -> p a' a b' b m aSource

request sends a value of type a' upstream and receives a value of type a.

(>\\) :: Monad m => (b' -> p a' a x' x m b) -> p b' b x' x m c -> p a' a x' x m cSource

(f >\\ p) replaces each request in p with f.

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

respond sends a value of type b downstream and receives a value of type b'.

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

(p //> f) replaces each respond in p with f.

pull :: (Monad m, Proxy p) => a' -> p a' a a' a m rSource

pull = request >=> respond >=> pull

(->>) :: Monad m => (b' -> p a' a b' b m r) -> p b' b c' c m r -> p a' a c' c m rSource

(f ->> p) pairs each request in p with a respond in f.

push :: (Monad m, Proxy p) => a -> p a' a a' a m rSource

push = respond >=> request >=> push

(>>~) :: Monad m => p a' a b' b m r -> (b -> p b' b c' c m r) -> p a' a c' c m rSource

(p >>~ f) pairs each respond in p with a request in f.

turn :: Monad m => p a' a b' b m r -> p b b' a a' m rSource

turn swaps requests and responds

Instances

Proxy ProxyFast 
Proxy ProxyCorrect 
Proxy p => Proxy (IdentityP p) 
Proxy p => Proxy (CodensityP p) 
Proxy p => Proxy (MaybeP p) 
Proxy p => Proxy (EitherP e p) 
Proxy p => Proxy (ReaderP i p) 
Proxy p => Proxy (StateP s p) 
Proxy p => Proxy (WriterP w p) 

Composition operators

(>->) :: (Monad m, Proxy p) => (b' -> p a' a b' b m r) -> (c'_ -> p b' b c' c m r) -> c'_ -> p a' a c' c m rSource

"pull" composition

 (f >-> g) x = f ->> g x

Compose two proxies blocked on a respond, generating a new proxy blocked on a respond

(>~>) :: (Monad m, Proxy p) => (a_ -> p a' a b' b m r) -> (b -> p b' b c' c m r) -> a_ -> p a' a c' c m rSource

"push" composition

 (f >~> g) x = f x >>~ g

Compose two proxies blocked on a request, generating a new proxy blocked on a request

(\>\) :: (Monad m, Proxy p) => (b' -> p a' a x' x m b) -> (c' -> p b' b x' x m c) -> c' -> p a' a x' x m cSource

"request" composition

 (f \>\ g) x = f >\\ g x

Compose two folds, generating a new fold

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

"respond" composition

 (f />/ g) x = f x //> g

Compose two unfolds, generating a new unfold

Flipped operators

(<-<) :: (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 rSource

Equivalent to (>->) with the arguments flipped

(<~<) :: (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 rSource

Equivalent to (>~>) with the arguments flipped

(/</) :: (Monad m, Proxy p) => (c' -> p b' b x' x m c) -> (b' -> p a' a x' x m b) -> c' -> p a' a x' x m cSource

Equivalent to (\>\) with the arguments flipped

(\<\) :: (Monad m, Proxy 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'Source

Equivalent to (/>/) with the arguments flipped

(<<-) :: (Monad m, Proxy p) => p b' b c' c m r -> (b' -> p a' a b' b m r) -> p a' a c' c m rSource

Equivalent to (->>) with the arguments flipped

(~<<) :: (Monad m, Proxy p) => (b -> p b' b c' c m r) -> p a' a b' b m r -> p a' a c' c m rSource

Equivalent to (>>~) with the arguments flipped

(//<) :: (Monad m, Proxy p) => p b' b x' x m c -> (b' -> p a' a x' x m b) -> p a' a x' x m cSource

Equivalent to (>\\) with the arguments flipped

(<\\) :: (Monad m, Proxy p) => (b -> p x' x c' c m b') -> p x' x b' b m a' -> p x' x c' c m a'Source

Equivalent to (//>) with the arguments flipped

ListT Monad Transformers

The RespondT monad transformer is equivalent to ListT over the downstream output. The RespondT Kleisli category corresponds to the "respond" category.

The RequestT monad transformer is equivalent to ListT over the upstream output. The RequestT Kleisli category corresponds to the "request" category.

Unlike ListT from transformers, these monad transformers are correct by construction and always satisfy the monad and monad transformer laws.

newtype RespondT p a' a b' m b Source

A monad transformer over a proxy's downstream output

Constructors

RespondT 

Fields

runRespondT :: p a' a b' b m b'
 

Instances

Proxy p => MonadTrans (RespondT p a' a b') 
(Monad m, Proxy p) => Monad (RespondT p a' a b' m) 
(Monad m, Proxy p) => Functor (RespondT p a' a b' m) 
(Monad m, Proxy p, Monoid b') => MonadPlus (RespondT p a' a b' m) 
(Monad m, Proxy p) => Applicative (RespondT p a' a b' m) 
(Monad m, Proxy p, Monoid b') => Alternative (RespondT p a' a b' m) 
(MonadIO m, Proxy p) => MonadIO (RespondT p a' a b' m) 

newtype RequestT p a b' b m a' Source

A monad transformer over a proxy's upstream output

Constructors

RequestT 

Fields

runRequestT :: p a' a b' b m a
 

Instances

Proxy p => MonadTrans (RequestT p a' a b') 
(Monad m, Proxy p) => Monad (RequestT p a b' b m) 
(Monad m, Proxy p) => Functor (RequestT p a b' b m) 
(Monad m, Proxy p, Monoid a) => MonadPlus (RequestT p a b' b m) 
(Monad m, Proxy p) => Applicative (RequestT p a b' b m) 
(Monad m, Proxy p, Monoid a) => Alternative (RequestT p a b' b m) 
(MonadIO m, Proxy p) => MonadIO (RequestT p a b' b m) 

Synonyms

data C Source

The empty type, denoting a 'C'losed end

type Pipe p a b = p () a () bSource

A unidirectional Proxy.

type Producer p b = p C () () bSource

A Pipe that produces values

Producers never request.

type Consumer p a = p () a () CSource

A Pipe that consumes values

Consumers never respond.

type CoPipe p a' b' = p a' () b' ()Source

A Pipe where everything flows upstream

type CoProducer p a' = p a' () () CSource

A CoPipe that produces values flowing upstream

CoProducers never respond.

type CoConsumer p b' = p C () b' ()Source

A CoConsumer that consumes values flowing upstream

CoConsumers never request.

type Client p a' a = p a' a () CSource

Client a' a sends requests of type a' and receives responses of type a.

Clients never respond.

type Server p b' b = p C () b' bSource

Server b' b receives requests of type b' and sends responses of type b.

Servers never request.

type Session p = p C () () CSource

A self-contained Session, ready to be run by runProxy

Sessions never request or respond.

type ProduceT p = RespondT p C () ()Source

ProduceT is ListT over the downstream output

type CoProduceT p = RequestT p () () CSource

CoProduceT is ListT over the upstream output

Laws

First, all proxies sit at the intersection of five categories:

  • The Kleisli category (all proxies are monads)
 return >=> f = f

 f >=> return = f

 (f >=> g) >=> h = f >=> (g >=> h)
  • The "request" category
 request \>\ f = f

 f \>\ request = f

 (f \>\ g) \>\ h = f \>\ (g \>\ h)
  • The "respond" category
 respond />/ f = f

 f />/ respond = f

 (f />/ g) />/ h = f />/ (g />/ h)
  • The "pull" category
 pull >-> f = f

 f >-> pull = f

 (f >-> g) >-> h = (f >-> g) >-> h
  • The "push" category
 push >~> f = f

 f >~> push = f

 (f >~> g) >~> h = f >~> (g >~> h)

Second, (turn .) transforms each streaming category into its dual:

  • The "request" category
 turn . request = respond

 turn . (f \>\ g) = turn . f \<\ turn . g
  • The "respond" category
 turn . respond = request

 turn . (f />/ g) = turn . f /</ turn. g
  • The "pull" category
 turn . pull = push

 turn . (f >-> g) = turn . f <~< turn . g
  • The "push" category
 turn . push = pull

 turn . (f >~> g) = turn . f <-< turn . g

Third, all proxies are monad transformers and must satisfy the monad transformer laws, using:

  • lift = lift_P

Fourth, all proxies are functors in the category of monads and must satisfy the functor laws, using:

  • hoist = hoist_P

Fifth, (\>\) and (/>/) both define functors between Kleisli categories

 a \>\ (b >=> c) = (a \>\ b) >=> (a \>\ c)

 a \>\ return = return
 (b >=> c) />/ a = (b />/ a) >=> (c />/ a)

 return />/ a = return

Sixth, all proxies must satisfy these additional Proxy laws:

 p \>\ lift . f = lift . f

 p \>\ respond  = respond

 lift . f />/ p = lift . f

 request />/  p = request

 pull = request >=> respond >=> pull

 push = respond >=> request >=> push

 p1 >-> lift . f = lift . f

 p1 >-> (lift . f >=> respond >=> p2) = lift . f >=> respond >=> (p1 >-> p2)

 (lift . g >=> respond >=> p1) >-> (lift . f >=> request >=> lift . h >=> p2)
     = lift . (f >=> g >=> h) >=> (p1 >-> p2)

 (lift . g >=> request >=> p1) >-> (lift . f >=> request >=> p2)
     = lift . (f >=> g) >=> request >=> (p1 >~> p2)

 lift . f >~> p2 = lift . f

 (lift . f >=> request >=> p1) >~> p2 = lift . f >=> request >=> (p1 >~> p2)

 (lift . f >=> respond >=> lift . h >=> p1) >~> (lift . g >=> request >=> p2)
     = lift . (f >=> g >=> h) >=> (p1 >~> p2)

 (lift . f >=> respond >=> p1) >~> (lift . g >=> respond >=> p2)
     = lift . (f >=> g) >=> (p1 >-> p2)

Polymorphic proxies

The ProxyInternal and MonadPlusP type classes duplicate 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 (Proxy 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) => ...

ProxyInternal and MonadPlusP exist solely for internal type class plumbing and I discourage you from using the methods in these classes unless you enjoy making your code less readable. 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 ProxyInternal p whereSource

The (ProxyInternal p) constraint is (basically) equivalent to the following polymorphic constraint:

 (forall a' a b' b m . (Monad m)
     => Monad      (p a' a b' b m)
     ,  MonadTrans (p a' a b' b  )
     ,  MFunctor   (p a' a b' b m)
     ,  MonadIO    (p a' a b' b m)
     ) => ...

Methods

return_P :: Monad m => r -> p a' a b' b m rSource

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

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

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

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

thread_P :: Monad m => p a' a b' b m r -> s -> p (a', s) (a, s) (b', s) (b, s) m (r, s)Source

class Proxy p => MonadPlusP p whereSource

The (MonadPlusP p) constraint is equivalent to the following polymorphic constraint:

 (forall a' a b' b m . (Monad m) => MonadPlus (p a' a b' b m)) => ...

Methods

mzero_P :: Monad m => p a' a b' b m rSource

mplus_P :: Monad m => p a' a b' b m r -> p a' a b' b m r -> p a' a b' b m rSource

Deprecated

These will be removed in version 4.0.0

idT :: (Monad m, Proxy p) => a' -> p a' a a' a m rSource

Deprecated: Use pull instead

coidT :: (Monad m, Proxy p) => a -> p a' a a' a m rSource

Deprecated: Use push instead

class Proxy p => ListT p Source

Deprecated: Use Proxy instead

runRespondK :: (q -> RespondT p a' a b' m b) -> q -> p a' a b' b m b'Source

Deprecated: Use '(runRespondT .)' instead

runRequestK :: (q -> RequestT p a b' b m a') -> q -> p a' a b' b m aSource

Deprecated: Use '(runRequestK .)' instead