{-| The core functionality for the 'Proxy' monad transformer Read "Pipes.Tutorial" if you want a beginners tutorial explaining how to use this library. The documentation in this module targets more advanced users who want to understand the theory behind this library. This module is not exported by default, and I recommend you use the unidirectional operations exported by the "Pipes" module if you can. You should only use this module if you require advanced features like: * bidirectional communication, or: * push-based 'Pipe's. -} {-# LANGUAGE RankNTypes, Trustworthy #-} module Pipes.Core ( -- * Proxy Monad Transformer -- $proxy Proxy , runEffect -- * Categories -- $categories -- ** Respond -- $respond , respond , (/>/) , (//>) -- ** Request -- $request , request , (\>\) , (>\\) -- ** Push -- $push , push , (>~>) , (>>~) -- ** Pull -- $pull , pull , (>+>) , (+>>) -- ** Reflect -- $reflect , reflect -- * Concrete Type Synonyms , X , Effect , Producer , Pipe , Consumer , Client , Server -- * Polymorphic Type Synonyms , Effect' , Producer' , Consumer' , Client' , Server' -- * Flipped operators , (\<\) , (/</) , (<~<) , (~<<) , (<+<) , (<\\) , (//<) , (<<+) -- * Re-exports , closed ) where import Pipes.Internal (Proxy(..), X, closed) {- $proxy Diagrammatically, you can think of a 'Proxy' as having the following shape: @ Upstream | Downstream +---------+ | | a' <== <== b' | | a ==> ==> b | | | +----|----+ v r @ You can connect proxies together in five different ways: * ('Pipes.>+>'): connect pull-based streams * ('Pipes.>~>'): connect push-based streams * ('Pipes.\>\'): chain folds * ('Pipes./>/'): chain unfolds * ('Control.Monad.>=>'): sequence proxies -} -- | Run a self-contained 'Effect', converting it back to the base monad runEffect :: Monad m => Effect m r -> m r runEffect = go where go p = case p of Request v _ -> closed v Respond v _ -> closed v M m -> m >>= go Pure r -> return r {-# INLINABLE runEffect #-} {- * Keep proxy composition lower in precedence than function composition, which is 9 at the time of of this comment, so that users can write things like: > lift . k >+> p > > hoist f . k >+> p * Keep the priorities different so that users can mix composition operators like: > up \>\ p />/ dn > > up >~> p >+> dn * Keep 'request' and 'respond' composition lower in precedence than 'pull' and 'push' composition, so that users can do: > read \>\ pull >+> writer * I arbitrarily choose a lower priority for downstream operators so that lazy pull-based computations need not evaluate upstream stages unless absolutely necessary. -} infixl 3 //> infixr 3 <\\ -- GHC will raise a parse error if either of these lines ends infixr 4 />/, >\\ -- with '\', which is why this comment is here infixl 4 \<\, //< infixl 5 \>\ -- Same thing here infixr 5 /</ infixl 6 <<+ infixr 6 +>> infixl 7 >+>, >>~ infixr 7 <+<, ~<< infixl 8 <~< infixr 8 >~> {- $categories A 'Control.Category.Category' is a set of components that you can connect with a composition operator, ('Control.Category..'), that has an identity, 'Control.Category.id'. The ('Control.Category..') and 'Control.Category.id' must satisfy the following three 'Control.Category.Category' laws: @ \-\- Left identity 'Control.Category.id' 'Control.Category..' f = f \-\- Right identity f 'Control.Category..' 'Control.Category.id' = f \-\- Associativity (f 'Control.Category..' g) 'Control.Category..' h = f 'Control.Category..' (g 'Control.Category..' h) @ The 'Proxy' type sits at the intersection of five separate categories, four of which are named after their identity: @ Identity | Composition | Point-ful +-------------+-------------+-------------+ respond category | 'respond' | '/>/' | '//>' | request category | 'request' | '\>\' | '>\\' | push category | 'push' | '>~>' | '>>~' | pull category | 'pull' | '>+>' | '+>>' | Kleisli category | 'return' | 'Control.Monad.>=>' | '>>=' | +-------------+-------------+-------------+ @ Each composition operator has a \"point-ful\" version, analogous to how ('>>=') is the point-ful version of ('Control.Monad.>=>'). For example, ('//>') is the point-ful version of ('/>/'). The convention is that the odd character out faces the argument that is a function. -} {- $respond The 'respond' category closely corresponds to the generator design pattern. The 'respond' category obeys the category laws, where 'respond' is the identity and ('/>/') is composition: @ \-\- Left identity 'respond' '/>/' f = f \-\- Right identity f '/>/' 'respond' = f \-\- Associativity (f '/>/' g) '/>/' h = f '/>/' (g '/>/' h) @ #respond-diagram# The following diagrams show the flow of information: @ 'respond' :: 'Monad' m => a -> 'Proxy' x' x a' a m a' \ a | +----|----+ | | | x' <== \\ /==== a' | X | x ==> / \\===> a | | | +----|----+ v a' ('/>/') :: 'Monad' m => (a -> 'Proxy' x' x b' b m a') -> (b -> 'Proxy' x' x c' c m b') -> (a -> 'Proxy' x' x c' c m a') \ a /===> b a | / | | +----|----+ / +----|----+ +----|----+ | v | / | v | | v | x' <== <== b' <==\\ / x'<== <== c' x' <== <== c' | f | X | g | = | f '/>/' g | x ==> ==> b ===/ \\ x ==> ==> c x ==> ==> c' | | | \\ | | | | | | +----|----+ \\ +----|----+ +----|----+ v \\ v v a' \\==== b' a' ('//>') :: 'Monad' m => 'Proxy' x' x b' b m a' -> (b -> 'Proxy' x' x c' c m b') -> 'Proxy' x' x c' c m a' \ /===> b / | +---------+ / +----|----+ +---------+ | | / | v | | | x' <== <== b' <==\\ / x'<== <== c' x' <== <== c' | f | X | g | = | f '//>' g | x ==> ==> b ===/ \\ x ==> ==> c x ==> ==> c' | | | \\ | | | | | | +----|----+ \\ +----|----+ +----|----+ v \\ v v a' \\==== b' a' @ -} {-| Send a value of type @a@ downstream and block waiting for a reply of type @a'@ 'respond' is the identity of the respond category. -} respond :: Monad m => a -> Proxy x' x a' a m a' respond a = Respond a Pure {-# INLINABLE [1] respond #-} {-| Compose two unfolds, creating a new unfold @ (f '/>/' g) x = f x '//>' g @ ('/>/') is the composition operator of the respond category. -} (/>/) :: Monad m => (a -> Proxy x' x b' b m a') -- ^ -> (b -> Proxy x' x c' c m b') -- ^ -> (a -> Proxy x' x c' c m a') -- ^ (fa />/ fb) a = fa a //> fb {-# INLINABLE (/>/) #-} {-| @(p \/\/> f)@ replaces each 'respond' in @p@ with @f@. Point-ful version of ('/>/') -} (//>) :: Monad m => Proxy x' x b' b m a' -- ^ -> (b -> Proxy x' x c' c m b') -- ^ -> Proxy x' x c' c m a' -- ^ p0 //> fb = go p0 where go p = case p of Request x' fx -> Request x' (\x -> go (fx x)) Respond b fb' -> fb b >>= \b' -> go (fb' b') M m -> M (m >>= \p' -> return (go p')) Pure a -> Pure a {-# INLINE [1] (//>) #-} {-# RULES "(Request x' fx ) //> fb" forall x' fx fb . (Request x' fx ) //> fb = Request x' (\x -> fx x //> fb); "(Respond b fb') //> fb" forall b fb' fb . (Respond b fb') //> fb = fb b >>= \b' -> fb' b' //> fb; "(M m ) //> fb" forall m fb . (M m ) //> fb = M (m >>= \p' -> return (p' //> fb)); "(Pure a ) //> fb" forall a fb . (Pure a ) //> fb = Pure a; #-} {- $request The 'request' category closely corresponds to the iteratee design pattern. The 'request' category obeys the category laws, where 'request' is the identity and ('\>\') is composition: @ -- Left identity 'request' '\>\' f = f \-\- Right identity f '\>\' 'request' = f \-\- Associativity (f '\>\' g) '\>\' h = f '\>\' (g '\>\' h) @ #request-diagram# The following diagrams show the flow of information: @ 'request' :: 'Monad' m => a' -> 'Proxy' a' a y' y m a \ a' | +----|----+ | | | a' <=====/ <== y' | | a ======\\ ==> y | | | +----|----+ v a ('\>\') :: 'Monad' m => (b' -> 'Proxy' a' a y' y m b) -> (c' -> 'Proxy' b' b y' y m c) -> (c' -> 'Proxy' a' a y' y m c) \ b'<=====\\ c' c' | \\ | | +----|----+ \\ +----|----+ +----|----+ | v | \\ | v | | v | a' <== <== y' \\== b' <== <== y' a' <== <== y' | f | | g | = | f '\>\' g | a ==> ==> y /=> b ==> ==> y a ==> ==> y | | | / | | | | | | +----|----+ / +----|----+ +----|----+ v / v v b ======/ c c ('>\\') :: Monad m => (b' -> Proxy a' a y' y m b) -> Proxy b' b y' y m c -> Proxy a' a y' y m c \ b'<=====\\ | \\ +----|----+ \\ +---------+ +---------+ | v | \\ | | | | a' <== <== y' \\== b' <== <== y' a' <== <== y' | f | | g | = | f '>\\' g | a ==> ==> y /=> b ==> ==> y a ==> ==> y | | | / | | | | | | +----|----+ / +----|----+ +----|----+ v / v v b ======/ c c @ -} {-| Send a value of type @a'@ upstream and block waiting for a reply of type @a@ 'request' is the identity of the request category. -} request :: Monad m => a' -> Proxy a' a y' y m a request a' = Request a' Pure {-# INLINABLE [1] request #-} {-| Compose two folds, creating a new fold @ (f '\>\' g) x = f '>\\' g x @ ('\>\') is the composition operator of the request category. -} (\>\) :: Monad m => (b' -> Proxy a' a y' y m b) -- ^ -> (c' -> Proxy b' b y' y m c) -- ^ -> (c' -> Proxy a' a y' y m c) -- ^ (fb' \>\ fc') c' = fb' >\\ fc' c' {-# INLINABLE (\>\) #-} {-| @(f >\\\\ p)@ replaces each 'request' in @p@ with @f@. Point-ful version of ('\>\') -} (>\\) :: Monad m => (b' -> Proxy a' a y' y m b) -- ^ -> Proxy b' b y' y m c -- ^ -> Proxy a' a y' y m c -- ^ fb' >\\ p0 = go p0 where go p = case p of Request b' fb -> fb' b' >>= \b -> go (fb b) Respond x fx' -> Respond x (\x' -> go (fx' x')) M m -> M (m >>= \p' -> return (go p')) Pure a -> Pure a {-# INLINE [1] (>\\) #-} {-# RULES "fb' >\\ (Request b' fb )" forall fb' b' fb . fb' >\\ (Request b' fb ) = fb' b' >>= \b -> fb' >\\ fb b; "fb' >\\ (Respond x fx')" forall fb' x fx' . fb' >\\ (Respond x fx') = Respond x (\x' -> fb' >\\ fx' x'); "fb' >\\ (M m )" forall fb' m . fb' >\\ (M m ) = M (m >>= \p' -> return (fb' >\\ p')); "fb' >\\ (Pure a )" forall fb' a . fb' >\\ (Pure a ) = Pure a; #-} {- $push The 'push' category closely corresponds to push-based Unix pipes. The 'push' category obeys the category laws, where 'push' is the identity and ('>~>') is composition: @ \-\- Left identity 'push' '>~>' f = f \-\- Right identity f '>~>' 'push' = f \-\- Associativity (f '>~>' g) '>~>' h = f '>~>' (g '>~>' h) @ The following diagram shows the flow of information: @ 'push' :: 'Monad' m => a -> 'Proxy' a' a a' a m r \ a | +----|----+ | v | a' <============ a' | | a ============> a | | | +----|----+ v r ('>~>') :: 'Monad' m => (a -> 'Proxy' a' a b' b m r) -> (b -> 'Proxy' b' b c' c m r) -> (a -> 'Proxy' a' a c' c m r) \ a b a | | | +----|----+ +----|----+ +----|----+ | v | | v | | v | a' <== <== b' <== <== c' a' <== <== c' | f | | g | = | f '>~>' g | a ==> ==> b ==> ==> c a ==> ==> c | | | | | | | | | +----|----+ +----|----+ +----|----+ v v v r r r @ -} {-| Forward responses followed by requests @ 'push' = 'respond' 'Control.Monad.>=>' 'request' 'Control.Monad.>=>' 'push' @ 'push' is the identity of the push category. -} push :: Monad m => a -> Proxy a' a a' a m r push = go where go a = Respond a (\a' -> Request a' go) {-# INLINABLE [1] push #-} {-| Compose two proxies blocked while 'request'ing data, creating a new proxy blocked while 'request'ing data @ (f '>~>' g) x = f x '>>~' g @ ('>~>') is the composition operator of the push category. -} (>~>) :: Monad m => (_a -> Proxy a' a b' b m r) -- ^ -> ( b -> Proxy b' b c' c m r) -- ^ -> (_a -> Proxy a' a c' c m r) -- ^ (fa >~> fb) a = fa a >>~ fb {-# INLINABLE (>~>) #-} {-| @(p >>~ f)@ pairs each 'respond' in @p@ with a 'request' in @f@. Point-ful version of ('>~>') -} (>>~) :: Monad m => Proxy a' a b' b m r -- ^ -> (b -> Proxy b' b c' c m r) -- ^ -> Proxy a' a c' c m r -- ^ p >>~ fb = case p of Request a' fa -> Request a' (\a -> fa a >>~ fb) Respond b fb' -> fb' +>> fb b M m -> M (m >>= \p' -> return (p' >>~ fb)) Pure r -> Pure r {-# INLINE [1] (>>~) #-} {- $pull The 'pull' category closely corresponds to pull-based Unix pipes. The 'pull' category obeys the category laws, where 'pull' is the identity and ('>+>') is composition: @ \-\- Left identity 'pull' '>+>' f = f \-\- Right identity f '>+>' 'pull' = f \-\- Associativity (f '>+>' g) '>+>' h = f '>+>' (g '>+>' h) @ #pull-diagram# The following diagrams show the flow of information: @ 'pull' :: 'Monad' m => a' -> 'Proxy' a' a a' a m r \ a' | +----|----+ | v | a' <============ a' | | a ============> a | | | +----|----+ v r ('>+>') :: 'Monad' m -> (b' -> 'Proxy' a' a b' b m r) -> (c' -> 'Proxy' b' b c' c m r) -> (c' -> 'Proxy' a' a c' c m r) \ b' c' c' | | | +----|----+ +----|----+ +----|----+ | v | | v | | v | a' <== <== b' <== <== c' a' <== <== c' | f | | g | = | f >+> g | a ==> ==> b ==> ==> c a ==> ==> c | | | | | | | | | +----|----+ +----|----+ +----|----+ v v v r r r @ -} {-| Forward requests followed by responses: @ 'pull' = 'request' 'Control.Monad.>=>' 'respond' 'Control.Monad.>=>' 'pull' @ 'pull' is the identity of the pull category. -} pull :: Monad m => a' -> Proxy a' a a' a m r pull = go where go a' = Request a' (\a -> Respond a go) {-# INLINABLE [1] pull #-} {-| Compose two proxies blocked in the middle of 'respond'ing, creating a new proxy blocked in the middle of 'respond'ing @ (f '>+>' g) x = f '+>>' g x @ ('>+>') is the composition operator of the pull category. -} (>+>) :: Monad m => ( b' -> Proxy a' a b' b m r) -- ^ -> (_c' -> Proxy b' b c' c m r) -- ^ -> (_c' -> Proxy a' a c' c m r) -- ^ (fb' >+> fc') c' = fb' +>> fc' c' {-# INLINABLE (>+>) #-} {-| @(f +>> p)@ pairs each 'request' in @p@ with a 'respond' in @f@. Point-ful version of ('>+>') -} (+>>) :: Monad m => (b' -> Proxy a' a b' b m r) -- ^ -> Proxy b' b c' c m r -- ^ -> Proxy a' a c' c m r -- ^ fb' +>> p = case p of Request b' fb -> fb' b' >>~ fb Respond c fc' -> Respond c (\c' -> fb' +>> fc' c') M m -> M (m >>= \p' -> return (fb' +>> p')) Pure r -> Pure r {-# INLINABLE [1] (+>>) #-} {- $reflect @(reflect .)@ transforms each streaming category into its dual: * The request category is the dual of the respond category @ 'reflect' '.' 'respond' = 'request' 'reflect' '.' (f '/>/' g) = 'reflect' '.' f '/</' 'reflect' '.' g @ @ 'reflect' '.' 'request' = 'respond' 'reflect' '.' (f '\>\' g) = 'reflect' '.' f '\<\' 'reflect' '.' g @ * The pull category is the dual of the push category @ 'reflect' '.' 'push' = 'pull' 'reflect' '.' (f '>~>' g) = 'reflect' '.' f '<+<' 'reflect' '.' g @ @ 'reflect' '.' 'pull' = 'push' 'reflect' '.' (f '>+>' g) = 'reflect' '.' f '<~<' 'reflect' '.' g @ -} -- | Switch the upstream and downstream ends reflect :: Monad m => Proxy a' a b' b m r -> Proxy b b' a a' m r reflect = go where go p = case p of Request a' fa -> Respond a' (\a -> go (fa a )) Respond b fb' -> Request b (\b' -> go (fb' b')) M m -> M (m >>= \p' -> return (go p')) Pure r -> Pure r {-# INLINABLE reflect #-} {-| An effect in the base monad 'Effect's neither 'Pipes.await' nor 'Pipes.yield' -} type Effect = Proxy X () () X -- | 'Producer's can only 'Pipes.yield' type Producer b = Proxy X () () b -- | 'Pipe's can both 'Pipes.await' and 'Pipes.yield' type Pipe a b = Proxy () a () b -- | 'Consumer's can only 'Pipes.await' type Consumer a = Proxy () a () X {-| @Client a' a@ sends requests of type @a'@ and receives responses of type @a@. 'Client's only 'request' and never 'respond'. -} type Client a' a = Proxy a' a () X {-| @Server b' b@ receives requests of type @b'@ and sends responses of type @b@. 'Server's only 'respond' and never 'request'. -} type Server b' b = Proxy X () b' b -- | Like 'Effect', but with a polymorphic type type Effect' m r = forall x' x y' y . Proxy x' x y' y m r -- | Like 'Producer', but with a polymorphic type type Producer' b m r = forall x' x . Proxy x' x () b m r -- | Like 'Consumer', but with a polymorphic type type Consumer' a m r = forall y' y . Proxy () a y' y m r -- | Like 'Server', but with a polymorphic type type Server' b' b m r = forall x' x . Proxy x' x b' b m r -- | Like 'Client', but with a polymorphic type type Client' a' a m r = forall y' y . Proxy a' a y' y m r -- | Equivalent to ('/>/') with the arguments flipped (\<\) :: Monad m => (b -> Proxy x' x c' c m b') -- ^ -> (a -> Proxy x' x b' b m a') -- ^ -> (a -> Proxy x' x c' c m a') -- ^ p1 \<\ p2 = p2 />/ p1 {-# INLINABLE (\<\) #-} -- | Equivalent to ('\>\') with the arguments flipped (/</) :: Monad m => (c' -> Proxy b' b x' x m c) -- ^ -> (b' -> Proxy a' a x' x m b) -- ^ -> (c' -> Proxy a' a x' x m c) -- ^ p1 /</ p2 = p2 \>\ p1 {-# INLINABLE (/</) #-} -- | Equivalent to ('>~>') with the arguments flipped (<~<) :: Monad m => (b -> Proxy b' b c' c m r) -- ^ -> (a -> Proxy a' a b' b m r) -- ^ -> (a -> Proxy a' a c' c m r) -- ^ p1 <~< p2 = p2 >~> p1 {-# INLINABLE (<~<) #-} -- | Equivalent to ('>+>') with the arguments flipped (<+<) :: Monad m => (c' -> Proxy b' b c' c m r) -- ^ -> (b' -> Proxy a' a b' b m r) -- ^ -> (c' -> Proxy a' a c' c m r) -- ^ p1 <+< p2 = p2 >+> p1 {-# INLINABLE (<+<) #-} -- | Equivalent to ('//>') with the arguments flipped (<\\) :: Monad m => (b -> Proxy x' x c' c m b') -- ^ -> Proxy x' x b' b m a' -- ^ -> Proxy x' x c' c m a' -- ^ f <\\ p = p //> f {-# INLINABLE (<\\) #-} -- | Equivalent to ('>\\') with the arguments flipped (//<) :: Monad m => Proxy b' b y' y m c -- ^ -> (b' -> Proxy a' a y' y m b) -- ^ -> Proxy a' a y' y m c -- ^ p //< f = f >\\ p {-# INLINABLE (//<) #-} -- | Equivalent to ('>>~') with the arguments flipped (~<<) :: Monad m => (b -> Proxy b' b c' c m r) -- ^ -> Proxy a' a b' b m r -- ^ -> Proxy a' a c' c m r -- ^ k ~<< p = p >>~ k {-# INLINABLE (~<<) #-} -- | Equivalent to ('+>>') with the arguments flipped (<<+) :: Monad m => Proxy b' b c' c m r -- ^ -> (b' -> Proxy a' a b' b m r) -- ^ -> Proxy a' a c' c m r -- ^ k <<+ p = p +>> k {-# INLINABLE (<<+) #-} {-# RULES "(p //> f) //> g" forall p f g . (p //> f) //> g = p //> (\x -> f x //> g) ; "p //> respond" forall p . p //> respond = p ; "respond x //> f" forall x f . respond x //> f = f x ; "f >\\ (g >\\ p)" forall f g p . f >\\ (g >\\ p) = (\x -> f >\\ g x) >\\ p ; "request >\\ p" forall p . request >\\ p = p ; "f >\\ request x" forall f x . f >\\ request x = f x ; "(p >>~ f) >>~ g" forall p f g . (p >>~ f) >>~ g = p >>~ (\x -> f x >>~ g) ; "p >>~ push" forall p . p >>~ push = p ; "push x >>~ f" forall x f . push x >>~ f = f x ; "f +>> (g +>> p)" forall f g p . f +>> (g +>> p) = (\x -> f +>> g x) +>> p ; "pull +>> p" forall p . pull +>> p = p ; "f +>> pull x" forall f x . f +>> pull x = f x #-}