{-# LANGUAGE CPP                        #-}
{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase                 #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeInType                 #-}
{-# LANGUAGE TypeOperators              #-}
{-# OPTIONS_HADDOCK not-home            #-}

-- |
-- Module      : Data.Conduino.Internal
-- Copyright   : (c) Justin Le 2019
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Internal module exposing the internals of 'Pipe', including its
-- underlying representation and base functor.
--
module Data.Conduino.Internal (
    Pipe(..)
  , PipeF(..)
  , awaitEither
  , yield
  , trimapPipe, mapInput, mapOutput, mapUpRes
  , hoistPipe
  , RecPipe
  , toRecPipe, fromRecPipe
  , withRecPipe
  , runStateP
  , pAwaitF, pYieldF
  ) where

import           Control.Applicative
import           Control.Monad.Catch
import           Control.Monad.Except
import           Control.Monad.Free.Class
import           Control.Monad.Free.TH
import           Control.Monad.RWS
import           Control.Monad.Trans.Free        (FreeT(..), FreeF(..))
import           Control.Monad.Trans.Free.Church
import           Control.Monad.Trans.State
import           Data.Functor

#if !MIN_VERSION_base(4,13,0)
import           Control.Monad.Fail
#endif

-- | Base functor of 'Pipe'.
--
-- A pipe fundamentally has the ability to await and the ability to yield.
-- The other functionality are implemented.
--
-- *  Lifting effects is implemented by the 'MonadTrans' and 'MonadIO'
--    instances that 'FT' gives.
-- *  /Ending/ with a result is implemented by the 'Applicative' instance's
--   'pure' that 'FT' gives.
-- *  Applicative and monadic sequenceing "after a pipe is done" is
--    implemented by the 'Applicative' and 'Monad' instances that 'FT'
--    gives.
--
-- On top of these we implement 'Data.Conduino..|' and other combinators
-- based on the structure that 'FT' gives.  For some functions, it can be
-- easier to use an alternative encoding, 'RecPipe', which is the same
-- thing but explicitly recursive.
data PipeF i o u a =
      PAwaitF (u -> a) (i -> a)
    | PYieldF o a
  deriving a -> PipeF i o u b -> PipeF i o u a
(a -> b) -> PipeF i o u a -> PipeF i o u b
(forall a b. (a -> b) -> PipeF i o u a -> PipeF i o u b)
-> (forall a b. a -> PipeF i o u b -> PipeF i o u a)
-> Functor (PipeF i o u)
forall a b. a -> PipeF i o u b -> PipeF i o u a
forall a b. (a -> b) -> PipeF i o u a -> PipeF i o u b
forall i o u a b. a -> PipeF i o u b -> PipeF i o u a
forall i o u a b. (a -> b) -> PipeF i o u a -> PipeF i o u b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> PipeF i o u b -> PipeF i o u a
$c<$ :: forall i o u a b. a -> PipeF i o u b -> PipeF i o u a
fmap :: (a -> b) -> PipeF i o u a -> PipeF i o u b
$cfmap :: forall i o u a b. (a -> b) -> PipeF i o u a -> PipeF i o u b
Functor

makeFree ''PipeF

-- | Similar to a conduit from the /conduit/ package.
--
-- For a @'Pipe' i o u m a@, you have:
--
-- *  @i@: Type of input stream (the things you can 'Data.Conduino.await')
-- *  @o@: Type of output stream (the things you 'yield')
-- *  @u@: Type of the /result/ of the upstream pipe (Outputted when
--    upstream pipe terminates)
-- *  @m@: Underlying monad (the things you can 'lift')
-- *  @a@: Result type when pipe terminates (outputted when finished, with
--    'pure' or 'return')
--
-- Some specializations:
--
-- *  If @i@ is @()@, the pipe is a /source/ --- it doesn't need anything
--    to produce items.  It will pump out items on its own, for pipes
--    downstream to receive and process.
--
-- *  If @o@ is 'Data.Void.Void', the pipe is a /sink/ --- it will never
--    'yield' anything downstream.  It will consume items from things
--    upstream, and produce a result (@a@) if and when it terminates.
--
-- *  If @u@ is 'Data.Void.Void', then the pipe's upstream is limitless,
--    and never terminates.  This means that you can use
--    'Data.Conduino.awaitSurely' instead of 'Data.Conduino.await', to get
--    await a value that is guaranteed to come.  You'll get an @i@ instead
--    of a @'Maybe' i@.
--
-- *  If @a@ is 'Data.Void.Void', then the pipe never terminates --- it
--    will keep on consuming and/or producing values forever.  If this is
--    a sink, it means that the sink will never terminate, and so
--    'Data.Conduino.runPipe' will also never terminate.  If it is
--    a source, it means that if you chain something downstream with
--    'Data.Conduino..|', that downstream pipe can use
--    'Data.Conduino.awaitSurely' to guarantee something being passed down.
--
-- Applicative and Monadic sequencing of pipes chains by exhaustion.
--
-- @
-- do pipeX
--    pipeY
--    pipeZ
-- @
--
-- is a pipe itself, that behaves like @pipeX@ until it terminates, then
-- @pipeY@ until it terminates, then @pipeZ@ until it terminates.  The
-- 'Monad' instance allows you to choose "which pipe to behave like next"
-- based on the terminating result of a previous pipe.
--
-- @
-- do x <- pipeX
--    pipeBasedOn x
-- @
--
-- Usually you would use it by chaining together pipes with
-- 'Data.Conduino..|' and then running the result with
-- 'Data.Conduino.runPipe'.
--
-- @
-- 'Data.Conduino.runPipe' $ someSource
--        'Data.Conduino..|' somePipe
--        .| someOtherPipe
--        .| someSink
-- @
--
-- See 'Data.Conduino..|' and 'Data.Conduino.runPipe' for more information
-- on usage.
--
-- For a "prelude" of commonly used 'Pipe's, see
-- "Data.Conduino.Combinators".
--
newtype Pipe i o u m a = Pipe { Pipe i o u m a -> FT (PipeF i o u) m a
pipeFree :: FT (PipeF i o u) m a }
  deriving
    ( a -> Pipe i o u m b -> Pipe i o u m a
(a -> b) -> Pipe i o u m a -> Pipe i o u m b
(forall a b. (a -> b) -> Pipe i o u m a -> Pipe i o u m b)
-> (forall a b. a -> Pipe i o u m b -> Pipe i o u m a)
-> Functor (Pipe i o u m)
forall a b. a -> Pipe i o u m b -> Pipe i o u m a
forall a b. (a -> b) -> Pipe i o u m a -> Pipe i o u m b
forall i o u (m :: * -> *) a b.
a -> Pipe i o u m b -> Pipe i o u m a
forall i o u (m :: * -> *) a b.
(a -> b) -> Pipe i o u m a -> Pipe i o u m b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Pipe i o u m b -> Pipe i o u m a
$c<$ :: forall i o u (m :: * -> *) a b.
a -> Pipe i o u m b -> Pipe i o u m a
fmap :: (a -> b) -> Pipe i o u m a -> Pipe i o u m b
$cfmap :: forall i o u (m :: * -> *) a b.
(a -> b) -> Pipe i o u m a -> Pipe i o u m b
Functor
    , Functor (Pipe i o u m)
a -> Pipe i o u m a
Functor (Pipe i o u m) =>
(forall a. a -> Pipe i o u m a)
-> (forall a b.
    Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b)
-> (forall a b c.
    (a -> b -> c)
    -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c)
-> (forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b)
-> (forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a)
-> Applicative (Pipe i o u m)
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a
Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b
(a -> b -> c) -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c
forall a. a -> Pipe i o u m a
forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a
forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
forall a b.
Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b
forall a b c.
(a -> b -> c) -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c
forall i o u (m :: * -> *). Functor (Pipe i o u m)
forall i o u (m :: * -> *) a. a -> Pipe i o u m a
forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a
forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
forall i o u (m :: * -> *) a b.
Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b
forall i o u (m :: * -> *) a b c.
(a -> b -> c) -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c
forall (f :: * -> *).
Functor f =>
(forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a
$c<* :: forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m a
*> :: Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
$c*> :: forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
liftA2 :: (a -> b -> c) -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c
$cliftA2 :: forall i o u (m :: * -> *) a b c.
(a -> b -> c) -> Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m c
<*> :: Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b
$c<*> :: forall i o u (m :: * -> *) a b.
Pipe i o u m (a -> b) -> Pipe i o u m a -> Pipe i o u m b
pure :: a -> Pipe i o u m a
$cpure :: forall i o u (m :: * -> *) a. a -> Pipe i o u m a
$cp1Applicative :: forall i o u (m :: * -> *). Functor (Pipe i o u m)
Applicative
    , Applicative (Pipe i o u m)
a -> Pipe i o u m a
Applicative (Pipe i o u m) =>
(forall a b.
 Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b)
-> (forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b)
-> (forall a. a -> Pipe i o u m a)
-> Monad (Pipe i o u m)
Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
forall a. a -> Pipe i o u m a
forall a b. Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
forall a b.
Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b
forall i o u (m :: * -> *). Applicative (Pipe i o u m)
forall i o u (m :: * -> *) a. a -> Pipe i o u m a
forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
forall i o u (m :: * -> *) a b.
Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b
forall (m :: * -> *).
Applicative m =>
(forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: a -> Pipe i o u m a
$creturn :: forall i o u (m :: * -> *) a. a -> Pipe i o u m a
>> :: Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
$c>> :: forall i o u (m :: * -> *) a b.
Pipe i o u m a -> Pipe i o u m b -> Pipe i o u m b
>>= :: Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b
$c>>= :: forall i o u (m :: * -> *) a b.
Pipe i o u m a -> (a -> Pipe i o u m b) -> Pipe i o u m b
$cp1Monad :: forall i o u (m :: * -> *). Applicative (Pipe i o u m)
Monad
    , m a -> Pipe i o u m a
(forall (m :: * -> *) a. Monad m => m a -> Pipe i o u m a)
-> MonadTrans (Pipe i o u)
forall i o u (m :: * -> *) a. Monad m => m a -> Pipe i o u m a
forall (m :: * -> *) a. Monad m => m a -> Pipe i o u m a
forall (t :: (* -> *) -> * -> *).
(forall (m :: * -> *) a. Monad m => m a -> t m a) -> MonadTrans t
lift :: m a -> Pipe i o u m a
$clift :: forall i o u (m :: * -> *) a. Monad m => m a -> Pipe i o u m a
MonadTrans
    , MonadFree (PipeF i o u)
    , Monad (Pipe i o u m)
Monad (Pipe i o u m) =>
(forall a. IO a -> Pipe i o u m a) -> MonadIO (Pipe i o u m)
IO a -> Pipe i o u m a
forall a. IO a -> Pipe i o u m a
forall i o u (m :: * -> *). MonadIO m => Monad (Pipe i o u m)
forall i o u (m :: * -> *) a. MonadIO m => IO a -> Pipe i o u m a
forall (m :: * -> *).
Monad m =>
(forall a. IO a -> m a) -> MonadIO m
liftIO :: IO a -> Pipe i o u m a
$cliftIO :: forall i o u (m :: * -> *) a. MonadIO m => IO a -> Pipe i o u m a
$cp1MonadIO :: forall i o u (m :: * -> *). MonadIO m => Monad (Pipe i o u m)
MonadIO
    , MonadState s
    , MonadReader r
    , MonadWriter w
    , MonadError e
    , MonadRWS r w s
    , Applicative (Pipe i o u m)
Pipe i o u m a
Applicative (Pipe i o u m) =>
(forall a. Pipe i o u m a)
-> (forall a. Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a)
-> (forall a. Pipe i o u m a -> Pipe i o u m [a])
-> (forall a. Pipe i o u m a -> Pipe i o u m [a])
-> Alternative (Pipe i o u m)
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
Pipe i o u m a -> Pipe i o u m [a]
Pipe i o u m a -> Pipe i o u m [a]
forall a. Pipe i o u m a
forall a. Pipe i o u m a -> Pipe i o u m [a]
forall a. Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
forall i o u (m :: * -> *).
Alternative m =>
Applicative (Pipe i o u m)
forall i o u (m :: * -> *) a. Alternative m => Pipe i o u m a
forall i o u (m :: * -> *) a.
Alternative m =>
Pipe i o u m a -> Pipe i o u m [a]
forall i o u (m :: * -> *) a.
Alternative m =>
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
forall (f :: * -> *).
Applicative f =>
(forall a. f a)
-> (forall a. f a -> f a -> f a)
-> (forall a. f a -> f [a])
-> (forall a. f a -> f [a])
-> Alternative f
many :: Pipe i o u m a -> Pipe i o u m [a]
$cmany :: forall i o u (m :: * -> *) a.
Alternative m =>
Pipe i o u m a -> Pipe i o u m [a]
some :: Pipe i o u m a -> Pipe i o u m [a]
$csome :: forall i o u (m :: * -> *) a.
Alternative m =>
Pipe i o u m a -> Pipe i o u m [a]
<|> :: Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
$c<|> :: forall i o u (m :: * -> *) a.
Alternative m =>
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
empty :: Pipe i o u m a
$cempty :: forall i o u (m :: * -> *) a. Alternative m => Pipe i o u m a
$cp1Alternative :: forall i o u (m :: * -> *).
Alternative m =>
Applicative (Pipe i o u m)
Alternative
    , Monad (Pipe i o u m)
Alternative (Pipe i o u m)
Pipe i o u m a
(Alternative (Pipe i o u m), Monad (Pipe i o u m)) =>
(forall a. Pipe i o u m a)
-> (forall a. Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a)
-> MonadPlus (Pipe i o u m)
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
forall a. Pipe i o u m a
forall a. Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
forall i o u (m :: * -> *). MonadPlus m => Monad (Pipe i o u m)
forall i o u (m :: * -> *).
MonadPlus m =>
Alternative (Pipe i o u m)
forall i o u (m :: * -> *) a. MonadPlus m => Pipe i o u m a
forall i o u (m :: * -> *) a.
MonadPlus m =>
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
forall (m :: * -> *).
(Alternative m, Monad m) =>
(forall a. m a) -> (forall a. m a -> m a -> m a) -> MonadPlus m
mplus :: Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
$cmplus :: forall i o u (m :: * -> *) a.
MonadPlus m =>
Pipe i o u m a -> Pipe i o u m a -> Pipe i o u m a
mzero :: Pipe i o u m a
$cmzero :: forall i o u (m :: * -> *) a. MonadPlus m => Pipe i o u m a
$cp2MonadPlus :: forall i o u (m :: * -> *). MonadPlus m => Monad (Pipe i o u m)
$cp1MonadPlus :: forall i o u (m :: * -> *).
MonadPlus m =>
Alternative (Pipe i o u m)
MonadPlus
    , Monad (Pipe i o u m)
e -> Pipe i o u m a
Monad (Pipe i o u m) =>
(forall e a. Exception e => e -> Pipe i o u m a)
-> MonadThrow (Pipe i o u m)
forall e a. Exception e => e -> Pipe i o u m a
forall i o u (m :: * -> *). MonadThrow m => Monad (Pipe i o u m)
forall i o u (m :: * -> *) e a.
(MonadThrow m, Exception e) =>
e -> Pipe i o u m a
forall (m :: * -> *).
Monad m =>
(forall e a. Exception e => e -> m a) -> MonadThrow m
throwM :: e -> Pipe i o u m a
$cthrowM :: forall i o u (m :: * -> *) e a.
(MonadThrow m, Exception e) =>
e -> Pipe i o u m a
$cp1MonadThrow :: forall i o u (m :: * -> *). MonadThrow m => Monad (Pipe i o u m)
MonadThrow
    , MonadThrow (Pipe i o u m)
MonadThrow (Pipe i o u m) =>
(forall e a.
 Exception e =>
 Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a)
-> MonadCatch (Pipe i o u m)
Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a
forall e a.
Exception e =>
Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a
forall i o u (m :: * -> *).
MonadCatch m =>
MonadThrow (Pipe i o u m)
forall i o u (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a
forall (m :: * -> *).
MonadThrow m =>
(forall e a. Exception e => m a -> (e -> m a) -> m a)
-> MonadCatch m
catch :: Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a
$ccatch :: forall i o u (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
Pipe i o u m a -> (e -> Pipe i o u m a) -> Pipe i o u m a
$cp1MonadCatch :: forall i o u (m :: * -> *).
MonadCatch m =>
MonadThrow (Pipe i o u m)
MonadCatch
    )

instance MonadFail m => MonadFail (Pipe i o u m) where
#if MIN_VERSION_base(4,13,0)
    fail :: String -> Pipe i o u m a
fail = m a -> Pipe i o u m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m a -> Pipe i o u m a)
-> (String -> m a) -> String -> Pipe i o u m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> m a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail
#else
    fail = lift . Control.Monad.Fail.fail
#endif

-- | Await on upstream output.  Will block until it receives an @i@
-- (expected input type) or a @u@ if the upstream pipe terminates.
awaitEither :: Pipe i o u m (Either u i)
awaitEither :: Pipe i o u m (Either u i)
awaitEither = Pipe i o u m (Either u i)
forall (m :: * -> *) i o u.
MonadFree (PipeF i o u) m =>
m (Either u i)
pAwaitF

-- | Send output downstream.
yield :: o -> Pipe i o u m ()
yield :: o -> Pipe i o u m ()
yield = o -> Pipe i o u m ()
forall (m :: * -> *) i o u. MonadFree (PipeF i o u) m => o -> m ()
pYieldF

-- | Map over the input type, output type, and upstream result type.
--
-- If you want to map over the result type, use 'fmap'.
trimapPipe
    :: (i -> j)
    -> (p -> o)
    -> (u -> v)
    -> Pipe j p v m a
    -> Pipe i o u m a
trimapPipe :: (i -> j)
-> (p -> o) -> (u -> v) -> Pipe j p v m a -> Pipe i o u m a
trimapPipe f :: i -> j
f g :: p -> o
g h :: u -> v
h = FT (PipeF i o u) m a -> Pipe i o u m a
forall i o u (m :: * -> *) a.
FT (PipeF i o u) m a -> Pipe i o u m a
Pipe (FT (PipeF i o u) m a -> Pipe i o u m a)
-> (Pipe j p v m a -> FT (PipeF i o u) m a)
-> Pipe j p v m a
-> Pipe i o u m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. PipeF j p v a -> PipeF i o u a)
-> FT (PipeF j p v) m a -> FT (PipeF i o u) m a
forall (f :: * -> *) (g :: * -> *) (m :: * -> *) b.
(forall a. f a -> g a) -> FT f m b -> FT g m b
transFT forall a. PipeF j p v a -> PipeF i o u a
go (FT (PipeF j p v) m a -> FT (PipeF i o u) m a)
-> (Pipe j p v m a -> FT (PipeF j p v) m a)
-> Pipe j p v m a
-> FT (PipeF i o u) m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pipe j p v m a -> FT (PipeF j p v) m a
forall i o u (m :: * -> *) a.
Pipe i o u m a -> FT (PipeF i o u) m a
pipeFree
  where
    go :: PipeF j p v a -> PipeF i o u a
go = \case
      PAwaitF a :: v -> a
a b :: j -> a
b -> (u -> a) -> (i -> a) -> PipeF i o u a
forall i o u a. (u -> a) -> (i -> a) -> PipeF i o u a
PAwaitF (v -> a
a (v -> a) -> (u -> v) -> u -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. u -> v
h) (j -> a
b (j -> a) -> (i -> j) -> i -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> j
f)
      PYieldF a :: p
a x :: a
x -> o -> a -> PipeF i o u a
forall i o u a. o -> a -> PipeF i o u a
PYieldF (p -> o
g p
a) a
x

-- | Transform the underlying monad of a pipe.
--
-- Note that if you are trying to work with monad transformers, this is
-- probably not what you want.  See "Data.Conduino.Lift" for tools for
-- working with underlying monad transformers.
hoistPipe
    :: (Monad m, Monad n)
    => (forall x. m x -> n x)
    -> Pipe i o u m a
    -> Pipe i o u n a
hoistPipe :: (forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe f :: forall x. m x -> n x
f = FT (PipeF i o u) n a -> Pipe i o u n a
forall i o u (m :: * -> *) a.
FT (PipeF i o u) m a -> Pipe i o u m a
Pipe (FT (PipeF i o u) n a -> Pipe i o u n a)
-> (Pipe i o u m a -> FT (PipeF i o u) n a)
-> Pipe i o u m a
-> Pipe i o u n a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall x. m x -> n x)
-> FT (PipeF i o u) m a -> FT (PipeF i o u) n a
forall (m :: * -> *) (n :: * -> *) (f :: * -> *) b.
(Monad m, Monad n) =>
(forall a. m a -> n a) -> FT f m b -> FT f n b
hoistFT forall x. m x -> n x
f (FT (PipeF i o u) m a -> FT (PipeF i o u) n a)
-> (Pipe i o u m a -> FT (PipeF i o u) m a)
-> Pipe i o u m a
-> FT (PipeF i o u) n a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pipe i o u m a -> FT (PipeF i o u) m a
forall i o u (m :: * -> *) a.
Pipe i o u m a -> FT (PipeF i o u) m a
pipeFree

-- | (Contravariantly) map over the expected input type.
mapInput :: (i -> j) -> Pipe j o u m a -> Pipe i o u m a
mapInput :: (i -> j) -> Pipe j o u m a -> Pipe i o u m a
mapInput f :: i -> j
f = (i -> j)
-> (o -> o) -> (u -> u) -> Pipe j o u m a -> Pipe i o u m a
forall i j p o u v (m :: * -> *) a.
(i -> j)
-> (p -> o) -> (u -> v) -> Pipe j p v m a -> Pipe i o u m a
trimapPipe i -> j
f o -> o
forall a. a -> a
id u -> u
forall a. a -> a
id

-- | Map over the downstream output type.
--
-- If you want to map over the result type, use 'fmap'.
mapOutput :: (p -> o) -> Pipe i p u m a -> Pipe i o u m a
mapOutput :: (p -> o) -> Pipe i p u m a -> Pipe i o u m a
mapOutput f :: p -> o
f = (i -> i)
-> (p -> o) -> (u -> u) -> Pipe i p u m a -> Pipe i o u m a
forall i j p o u v (m :: * -> *) a.
(i -> j)
-> (p -> o) -> (u -> v) -> Pipe j p v m a -> Pipe i o u m a
trimapPipe i -> i
forall a. a -> a
id p -> o
f u -> u
forall a. a -> a
id

-- | (Contravariantly) map over the upstream result type.
mapUpRes :: (u -> v) -> Pipe i o v m a -> Pipe i o u m a
mapUpRes :: (u -> v) -> Pipe i o v m a -> Pipe i o u m a
mapUpRes = (i -> i)
-> (o -> o) -> (u -> v) -> Pipe i o v m a -> Pipe i o u m a
forall i j p o u v (m :: * -> *) a.
(i -> j)
-> (p -> o) -> (u -> v) -> Pipe j p v m a -> Pipe i o u m a
trimapPipe i -> i
forall a. a -> a
id o -> o
forall a. a -> a
id

-- | A version of 'Pipe' that uses explicit, concrete recursion instead of
-- church-encoding like 'Pipe'.  Some functions --- especially ones that
-- combine multiple pipes into one --- are easier to implement in this
-- form.
type RecPipe i o u = FreeT (PipeF i o u)

-- | Convert from a 'Pipe' to a 'RecPipe'.  While most of this library is
-- defined in terms of 'Pipe', it can be easier to write certain low-level
-- pipe combining functions in terms of 'RecPipe' than 'Pipe'.
toRecPipe :: Monad m => Pipe i o u m a -> RecPipe i o u m a
toRecPipe :: Pipe i o u m a -> RecPipe i o u m a
toRecPipe = FT (PipeF i o u) m a -> RecPipe i o u m a
forall (m :: * -> *) (f :: * -> *) a.
(Monad m, Functor f) =>
FT f m a -> FreeT f m a
fromFT (FT (PipeF i o u) m a -> RecPipe i o u m a)
-> (Pipe i o u m a -> FT (PipeF i o u) m a)
-> Pipe i o u m a
-> RecPipe i o u m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pipe i o u m a -> FT (PipeF i o u) m a
forall i o u (m :: * -> *) a.
Pipe i o u m a -> FT (PipeF i o u) m a
pipeFree

-- | Convert a 'RecPipe' back into a 'Pipe'.
fromRecPipe :: Monad m => RecPipe i o u m a -> Pipe i o u m a
fromRecPipe :: RecPipe i o u m a -> Pipe i o u m a
fromRecPipe = FT (PipeF i o u) m a -> Pipe i o u m a
forall i o u (m :: * -> *) a.
FT (PipeF i o u) m a -> Pipe i o u m a
Pipe (FT (PipeF i o u) m a -> Pipe i o u m a)
-> (RecPipe i o u m a -> FT (PipeF i o u) m a)
-> RecPipe i o u m a
-> Pipe i o u m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RecPipe i o u m a -> FT (PipeF i o u) m a
forall (m :: * -> *) (f :: * -> *) a.
Monad m =>
FreeT f m a -> FT f m a
toFT

-- | Convenint wrapper over 'toRecPipe' and 'fromRecPipe'.
--
-- @since 0.2.1.0
withRecPipe
    :: (Monad m, Monad n)
    => (RecPipe i o u m a -> RecPipe j p v n b)
    -> Pipe i o u m a
    -> Pipe j p v n b
withRecPipe :: (RecPipe i o u m a -> RecPipe j p v n b)
-> Pipe i o u m a -> Pipe j p v n b
withRecPipe f :: RecPipe i o u m a -> RecPipe j p v n b
f = RecPipe j p v n b -> Pipe j p v n b
forall (m :: * -> *) i o u a.
Monad m =>
RecPipe i o u m a -> Pipe i o u m a
fromRecPipe (RecPipe j p v n b -> Pipe j p v n b)
-> (Pipe i o u m a -> RecPipe j p v n b)
-> Pipe i o u m a
-> Pipe j p v n b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RecPipe i o u m a -> RecPipe j p v n b
f (RecPipe i o u m a -> RecPipe j p v n b)
-> (Pipe i o u m a -> RecPipe i o u m a)
-> Pipe i o u m a
-> RecPipe j p v n b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pipe i o u m a -> RecPipe i o u m a
forall (m :: * -> *) i o u a.
Monad m =>
Pipe i o u m a -> RecPipe i o u m a
toRecPipe

-- | Turn a 'Pipe' that runs over 'StateT' into a "state-modifying 'Pipe'",
-- that returns the final state when it terminates.
--
-- The main usage of this is to "isolate" the state from other pipes in the
-- same chain.  For example, of @p@, @q@, and @r@ are all pipes under
-- 'StateT', then:
--
-- @
--     p
--  .| q
--  .| r
-- @
--
-- will all share underlying state, and each can modify the state that they
-- all three share.  We essentially have global state.
--
-- However, if you use 'runStateP', you can all have them use different
-- encapsulated states.
--
-- @
--     void (runStateP s0 p)
--  .| void (runStateP s1 q)
--  .| runStateP s2 r
-- @
--
-- In this case, each of those three chained pipes will use their own
-- internal states, without sharing.
--
-- This is also useful if you want to chain a pipe over 'StateT' with
-- pipes that don't use state at all: for example if @a@ and @b@ are
-- "non-stateful" pipes (/not/ over 'StateT'), you can do:
--
-- @
--     a
--  .| void (runStateP s1 q)
--  .| b
-- @
--
-- And @a@ and @b@ will be none the wiser to the fact that @q@ uses
-- 'StateT' internally.
--
-- Note to avoid the usage of 'void', 'Data.Conduino.Lift.evalStateP' might
-- be more useful.
--
-- @since 0.2.1.0
runStateP
    :: Monad m
    => s
    -> Pipe i o u (StateT s m) a
    -> Pipe i o u m (a, s)
runStateP :: s -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
runStateP = (RecPipe i o u (StateT s m) a -> RecPipe i o u m (a, s))
-> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
forall (m :: * -> *) (n :: * -> *) i o u a j p v b.
(Monad m, Monad n) =>
(RecPipe i o u m a -> RecPipe j p v n b)
-> Pipe i o u m a -> Pipe j p v n b
withRecPipe ((RecPipe i o u (StateT s m) a -> RecPipe i o u m (a, s))
 -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s))
-> (s -> RecPipe i o u (StateT s m) a -> RecPipe i o u m (a, s))
-> s
-> Pipe i o u (StateT s m) a
-> Pipe i o u m (a, s)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s -> RecPipe i o u (StateT s m) a -> RecPipe i o u m (a, s)
forall (m :: * -> *) (f :: * -> *) t a.
(Functor m, Functor f) =>
t -> FreeT f (StateT t m) a -> FreeT f m (a, t)
go
  where
    go :: t -> FreeT f (StateT t m) a -> FreeT f m (a, t)
go s :: t
s (FreeT p :: StateT t m (FreeF f a (FreeT f (StateT t m) a))
p) = m (FreeF f (a, t) (FreeT f m (a, t))) -> FreeT f m (a, t)
forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT (m (FreeF f (a, t) (FreeT f m (a, t))) -> FreeT f m (a, t))
-> m (FreeF f (a, t) (FreeT f m (a, t))) -> FreeT f m (a, t)
forall a b. (a -> b) -> a -> b
$ StateT t m (FreeF f a (FreeT f (StateT t m) a))
-> t -> m (FreeF f a (FreeT f (StateT t m) a), t)
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateT StateT t m (FreeF f a (FreeT f (StateT t m) a))
p t
s m (FreeF f a (FreeT f (StateT t m) a), t)
-> ((FreeF f a (FreeT f (StateT t m) a), t)
    -> FreeF f (a, t) (FreeT f m (a, t)))
-> m (FreeF f (a, t) (FreeT f m (a, t)))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(q :: FreeF f a (FreeT f (StateT t m) a)
q, s' :: t
s') ->
      case FreeF f a (FreeT f (StateT t m) a)
q of
        Pure x :: a
x -> (a, t) -> FreeF f (a, t) (FreeT f m (a, t))
forall (f :: * -> *) a b. a -> FreeF f a b
Pure (a
x, t
s')
        Free l :: f (FreeT f (StateT t m) a)
l -> f (FreeT f m (a, t)) -> FreeF f (a, t) (FreeT f m (a, t))
forall (f :: * -> *) a b. f b -> FreeF f a b
Free (f (FreeT f m (a, t)) -> FreeF f (a, t) (FreeT f m (a, t)))
-> f (FreeT f m (a, t)) -> FreeF f (a, t) (FreeT f m (a, t))
forall a b. (a -> b) -> a -> b
$ t -> FreeT f (StateT t m) a -> FreeT f m (a, t)
go t
s' (FreeT f (StateT t m) a -> FreeT f m (a, t))
-> f (FreeT f (StateT t m) a) -> f (FreeT f m (a, t))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (StateT t m) a)
l