{-| 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)
@

    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 b' b 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'
@

-}

{-| 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 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
{-# INLINABLE (//>) #-}

{-# 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)
@

    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
@
-}

{-| 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 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
{-# INLINABLE (>\\) #-}

{-# 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 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 an '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
{-# INLINABLE (>>~) #-}

{- $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)
@

    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 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 (+>>) #-}

{- $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

  #-}