Safe Haskell | Safe-Inferred |
---|
This module defines the theoretical framework underpinning this library
- class ProxyInternal p => Proxy p where
- request :: Monad m => a' -> p a' a b' b m a
- (>\\) :: Monad m => (b' -> p a' a x' x m b) -> p b' b x' x m c -> p a' a x' x m c
- respond :: Monad m => b -> p a' a b' b m 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'
- pull :: (Monad m, Proxy p) => a' -> p a' a a' a m r
- (->>) :: Monad m => (b' -> p a' a b' b m r) -> p b' b c' c m r -> p a' a c' c m r
- push :: (Monad m, Proxy p) => a -> p a' a a' a m r
- (>>~) :: Monad m => p a' a b' b m r -> (b -> p b' b c' c m r) -> p a' a c' c m r
- turn :: Monad m => p a' a b' b m r -> p b b' a a' m r
- (>->) :: (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 r
- (>~>) :: (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 r
- (\>\) :: (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 c
- (/>/) :: (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'
- (<-<) :: (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
- (/</) :: (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 c
- (\<\) :: (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'
- (<<-) :: (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 r
- (~<<) :: (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 r
- (//<) :: (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 c
- (<\\) :: (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'
- newtype RespondT p a' a b' m b = RespondT {
- runRespondT :: p a' a b' b m b'
- newtype RequestT p a b' b m a' = RequestT {
- runRequestT :: p a' a b' b m a
- data C
- type Pipe p a b = p () a () b
- type Producer p b = p C () () b
- type Consumer p a = p () a () C
- type CoPipe p a' b' = p a' () b' ()
- type CoProducer p a' = p a' () () C
- type CoConsumer p b' = p C () b' ()
- type Client p a' a = p a' a () C
- type Server p b' b = p C () b' b
- type Session p = p C () () C
- type ProduceT p = RespondT p C () ()
- type CoProduceT p = RequestT p () () C
- class ProxyInternal p where
- return_P :: Monad m => r -> p a' a b' b m r
- (?>=) :: Monad m => p a' a b' b m r -> (r -> p a' a b' b m r') -> p a' a b' b m r'
- lift_P :: Monad m => m r -> p a' a b' b m r
- hoist_P :: Monad m => (forall r. m r -> n r) -> p a' a b' b m r' -> p a' a b' b n r'
- liftIO_P :: MonadIO m => IO r -> p a' a b' b m r
- thread_P :: Monad m => p a' a b' b m r -> s -> p (a', s) (a, s) (b', s) (b, s) m (r, s)
- class Proxy p => MonadPlusP 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
- class Proxy p => ListT p
- runRespondK :: (q -> RespondT p a' a b' m b) -> q -> p a' a b' b m b'
- runRequestK :: (q -> RequestT p a b' b m a') -> q -> p a' a b' b m a
The Proxy Class
class ProxyInternal p => Proxy p whereSource
The Proxy
class defines a Monad
that intersects four streaming
categories:
- The "request" category:
request
and (\>\
) - The "respond" category:
respond
and (/>/
) - The "pull" category:
pull
and (>->
) - The "push" category:
push
and (>~>
)
This class requires the "point-ful" version of each category's composition operator for efficiency.
Minimal definition:
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
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
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
(>~>) :: (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
(\>\) :: (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
RespondT | |
|
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
RequestT | |
|
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
type CoProducer p a' = p a' () () CSource
A CoPipe
that produces values flowing upstream
CoProducer
s never respond
.
type CoConsumer p b' = p C () b' ()Source
A CoConsumer
that consumes values flowing upstream
CoConsumer
s never request
.
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) ) => ...
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
ProxyInternal ProxyFast | |
ProxyInternal ProxyCorrect | |
Proxy p => ProxyInternal (IdentityP p) | |
Proxy p => ProxyInternal (CodensityP p) | |
Proxy p => ProxyInternal (MaybeP p) | |
Proxy p => ProxyInternal (EitherP e p) | |
Proxy p => ProxyInternal (ReaderP i p) | |
Proxy p => ProxyInternal (StateP s p) | |
Proxy p => ProxyInternal (WriterP w p) |
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)) => ...
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
MonadPlusP p => MonadPlusP (IdentityP p) | |
MonadPlusP p => MonadPlusP (CodensityP p) | |
Proxy p => MonadPlusP (MaybeP p) | |
(Proxy p, Monoid e) => MonadPlusP (EitherP e p) | |
MonadPlusP p => MonadPlusP (ReaderP i p) | |
MonadPlusP p => MonadPlusP (StateP s p) | |
MonadPlusP p => MonadPlusP (WriterP w p) |
Deprecated
These will be removed in version 4.0.0
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