{-# LANGUAGE LambdaCase    #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns  #-}

-- |
-- Module      : Data.Conduino.Lift
-- Copyright   : (c) Justin Le 2019
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Working with underlying monad transformers and 'Pipe'.
--
-- There is no "general abstraction" for dealing with each monad
-- transformer, but we can translate the semantics that each monad
-- transformer provides into meaningful 'Pipe' operations.
--
-- For example, a @'Pipe' i o u ('State' s) a@ is a pipe working over
-- stateful effects --- it can pull information and modify an underlying
-- state to do its job.  It takes in @i@ and outputs @o@, using an
-- underlying state @s@.
--
-- However, such a pipe is similar to @s -> 'Pipe'
-- i o u 'Data.Functor.Identity.Identity' (a, s)@.  Giving some starting
-- state, it takes in @i@ and outputs @o@, and when it completes, it
-- returns an @a@ and an @s@, the final state after all its processing is
-- done.
--
-- The /general/ idea is that:
--
-- *  A pipe over a monad transformer /shares that monadic context/ over
--    /every pipe/ in a composition.
--
--    For example, if @p@, @q@, and @r@ are all pipes over 'StateT', the @p
--    .| q .| r@ will all share a common global state.
--
--    If @p@, @q@, and @r@ are all pipes over 'ExceptT', then @p .| q .| r@
--    will all short-circult fail each other: if @q@ fails, then they all
--    fail, etc.
--
--    If @p@, @q@, and @r@ are all pipes over 'WriterT' then @p .| q .| r@
--    will all accumulate to a shared global log.
--
--    If @p@, @q@, and @r@ are all pipes over 'ReaderT' then @p .| q .| r@
--    will use the same identical environment.
--
-- *  Using the @runX@ family of functions ('runStateP', 'runExceptP',
--    etc.) lets you /isolate/ out the common context within a composition
--    of pipes.
--
--    For example, if @p@ is a pipe over 'StateT', then @a .| 'void' ('runStateP'
--    s0 p) .| b@, @a@ and @b@ will not be able to use the state of @p@.
--
--    If @p@ is a pipe over 'ExceptT', then in @a .| void ('runExceptP' p) .|
--    b@, a failure in @p@ will not cause all the others to fail.
--
-- Both of these representations have different advantages and
-- disadvantages, that are separate and unique for each individual monad
-- transformer on a case-by-case basis.  This module provides functions on
-- such a case-by-case basis as you need them.
--
-- @since 0.2.1.0
module Data.Conduino.Lift (
  -- * State
  -- ** Lazy
    stateP, runStateP, evalStateP, execStateP
  -- ** Strict
  , statePS, runStatePS, evalStatePS, execStatePS
  -- * Except
  , exceptP, runExceptP, runExceptP_
  -- * Reader
  , readerP, runReaderP
  -- * Writer
  -- ** Lazy
  , writerP, runWriterP, execWriterP
  -- ** Strict
  , writerPS, runWriterPS, execWriterPS
  -- * RWS
  -- ** Lazy
  , rwsP, runRWSP, evalRWSP, execRWSP
  -- ** Strict
  , rwsPS, runRWSPS, evalRWSPS, execRWSPS
  -- * Catch
  , catchP, runCatchP
  ) where

import           Control.Monad
import           Control.Monad.Catch.Pure
import           Control.Monad.Trans.Class
import           Control.Monad.Trans.Except
import           Control.Monad.Trans.Free
import           Control.Monad.Trans.Free.Church
import           Control.Monad.Trans.RWS           (RWST(..))
import           Control.Monad.Trans.Reader
import           Control.Monad.Trans.State
import           Control.Monad.Trans.Writer
import           Data.Conduino
import           Data.Conduino.Internal
import           Data.Functor
import qualified Control.Monad.Trans.RWS           as RWS
import qualified Control.Monad.Trans.RWS.Strict    as RWSS
import qualified Control.Monad.Trans.State.Strict  as SS
import qualified Control.Monad.Trans.Writer.Strict as WS

-- | Turn a "state-modifying 'Pipe'" into a 'Pipe' that runs over 'StateT',
-- so you can chain it with other 'StateT' pipes.
--
-- Note that this will /overwrite/ whatever state exists with
-- the @s@ that it gets when it terminates.  If any other pipe in this
-- chain modifies or uses state, all modifications will be overwritten when
-- the @(a, s)@-producing pipe terminates.
--
-- @since 0.2.1.0
stateP
    :: Monad m
    => (s -> Pipe i o u m (a, s))
    -> Pipe i o u (StateT s m) a
stateP :: forall (m :: * -> *) s i o u a.
Monad m =>
(s -> Pipe i o u m (a, s)) -> Pipe i o u (StateT s m) a
stateP s -> Pipe i o u m (a, s)
f = do
    s
s       <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall (m :: * -> *) s. Monad m => StateT s m s
get
    (a
x, s
s') <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (s -> Pipe i o u m (a, s)
f s
s)
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) s. Monad m => s -> StateT s m ()
put s
s')

-- | Like 'runStateP', but ignoring the final result.  It returns the final
-- state after the pipe succesfuly terminates.
--
-- @since 0.2.1.0
execStateP
    :: Monad m
    => s
    -> Pipe i o u (StateT s m) a
    -> Pipe i o u m s
execStateP :: forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m s
execStateP s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
runStateP s
s
{-# INLINE execStateP #-}

-- | Takes a 'Pipe' over 'StateT' and "hides" the state from the outside
-- world.  Give an initial state --- the pipe behaves the same way, but to
-- the external user it is abstracted away.  See 'runStateP' for more
-- information.
--
-- This can be cleaner than 'runStateP' because if @a@ is @()@, you
-- don't have to sprinkle in 'void' everywhere.  However, it's only really
-- useful if you don't need to get the final state upon termination.
--
-- @since 0.2.1.0
evalStateP
    :: Monad m
    => s
    -> Pipe i o u (StateT s m) a
    -> Pipe i o u m a
evalStateP :: forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m a
evalStateP s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
runStateP s
s
{-# INLINE evalStateP #-}

-- | 'stateP', but for "Control.Monad.Trans.State.Strict".
--
-- @since 0.2.1.0
statePS
    :: Monad m
    => (s -> Pipe i o u m (a, s))
    -> Pipe i o u (SS.StateT s m) a
statePS :: forall (m :: * -> *) s i o u a.
Monad m =>
(s -> Pipe i o u m (a, s)) -> Pipe i o u (StateT s m) a
statePS s -> Pipe i o u m (a, s)
f = do
    s
s       <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall (m :: * -> *) s. Monad m => StateT s m s
SS.get
    (a
x, s
s') <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (s -> Pipe i o u m (a, s)
f s
s)
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) s. Monad m => s -> StateT s m ()
SS.put s
s')
{-# INLINE statePS #-}

-- | 'execStateP', but for "Control.Monad.Trans.State.Strict".
--
-- @since 0.2.1.0
execStatePS
    :: Monad m
    => s
    -> Pipe i o u (SS.StateT s m) a
    -> Pipe i o u m s
execStatePS :: forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m s
execStatePS s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
runStatePS s
s
{-# INLINE execStatePS #-}

-- | 'evalStateP', but for "Control.Monad.Trans.State.Strict".
--
-- @since 0.2.1.0
evalStatePS
    :: Monad m
    => s
    -> Pipe i o u (SS.StateT s m) a
    -> Pipe i o u m a
evalStatePS :: forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m a
evalStatePS s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) s i o u a.
Monad m =>
s -> Pipe i o u (StateT s m) a -> Pipe i o u m (a, s)
runStatePS s
s
{-# INLINE evalStatePS #-}

-- | Turn a "failable-result" 'Pipe' into a pipe over 'ExceptT'.
--
-- Note that a 'throwE' failure will only ever happen when the input pipe
-- "succesfully" terminates with 'Left'.  It would never happen before the
-- pipe terminates, since you don't get the @'Either' e a@ until the pipe
-- succesfully terminates.
--
-- @since 0.2.1.0
exceptP
    :: Monad m
    => Pipe i o u m (Either e a)
    -> Pipe i o u (ExceptT e m) a
exceptP :: forall (m :: * -> *) i o u e a.
Monad m =>
Pipe i o u m (Either e a) -> Pipe i o u (ExceptT e m) a
exceptP Pipe i o u m (Either e a)
p = forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Pipe i o u m (Either e a)
p forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left  e
e -> forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a. Monad m => e -> ExceptT e m a
throwE e
e
    Right a
x -> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
{-# INLINE exceptP #-}

-- | Turn a 'Pipe' that runs over 'ExceptT' into an "early-terminating
-- 'Pipe'" that "succesfully" returns 'Left' or 'Right'.
--
-- The main usage of this is to "isolate" the short-circuiting failure of
-- 'ExceptT' to only happen within one component of a chain.  For example,
-- of @p@, @q@, and @r@ are all pipes under 'ExceptT', then:
--
-- @
--     p
--  .| q
--  .| r
-- @
--
-- will short-circuit fail if /any/ of @p@, @q@, or @r@ fail.  We have
-- global failure only.
--
-- However, if you use 'runExceptP', we isolate the short-circuiting
-- failure to only a single type.
--
-- @
--     void (runExceptP p)
--  .| void (runExceptP q)
--  .| runExceptP r
-- @
--
-- In this case, if (for example) @q@ fails, it won't cause the whole thing
-- to fail: it will just be the same as if @q@ succesfully terminates
-- normally.
--
-- This is also useful if you want to chain a pipe over 'ExceptT' with
-- pipes that don't have 'ExceptT' at all: for example if @a@ and @b@ are
-- "non-erroring" pipes (/not/ over 'ExceptT'), you can do:
--
-- @
--     a
--  .| void (runExceptP q)
--  .| b
-- @
--
-- And @a@ and @b@ will be none the wiser to the fact that @q@ uses
-- 'ExceptT' internally.
--
-- Note to avoid the usage of 'void', 'runExceptP_' might be more useful.
--
-- @since 0.2.1.0
runExceptP
    :: Monad m
    => Pipe i o u (ExceptT e m) a
    -> Pipe i o u m (Either e a)
runExceptP :: forall (m :: * -> *) i o u e a.
Monad m =>
Pipe i o u (ExceptT e m) a -> Pipe i o u m (Either e a)
runExceptP (Pipe (FT forall r.
(a -> ExceptT e m r)
-> (forall x.
    (x -> ExceptT e m r) -> PipeF i o u x -> ExceptT e m r)
-> ExceptT e m r
f)) = forall i o u (m :: * -> *) a.
FT (PipeF i o u) m a -> Pipe i o u m a
Pipe forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) (m :: * -> *) a.
(forall r.
 (a -> m r) -> (forall x. (x -> m r) -> f x -> m r) -> m r)
-> FT f m a
FT forall a b. (a -> b) -> a -> b
$ \Either e a -> m r
pr forall x. (x -> m r) -> PipeF i o u x -> m r
bd -> forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Either e a -> m r
pr forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) forall (f :: * -> *) a. Applicative f => a -> f a
pure forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (
      forall r.
(a -> ExceptT e m r)
-> (forall x.
    (x -> ExceptT e m r) -> PipeF i o u x -> ExceptT e m r)
-> ExceptT e m r
f (forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either e a -> m r
pr forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. b -> Either a b
Right)
        (\x -> ExceptT e m r
c -> forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall x. (x -> m r) -> PipeF i o u x -> m r
bd (forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Either e a -> m r
pr forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) forall (f :: * -> *) a. Applicative f => a -> f a
pure forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT forall b c a. (b -> c) -> (a -> b) -> a -> c
. x -> ExceptT e m r
c))
    )
{-# INLINE runExceptP #-}

-- | A handy version of 'runExceptP' that discards its output, so it can be
-- easier to chain using '.|'.  It's useful if you are using 'runExceptP'
-- to "isolate" failures from the rest of a chain.
--
-- @since 0.2.1.0
runExceptP_
    :: Monad m
    => Pipe i o u (ExceptT e m) a
    -> Pipe i o u m ()
runExceptP_ :: forall (m :: * -> *) i o u e a.
Monad m =>
Pipe i o u (ExceptT e m) a -> Pipe i o u m ()
runExceptP_ = forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) i o u e a.
Monad m =>
Pipe i o u (ExceptT e m) a -> Pipe i o u m (Either e a)
runExceptP
{-# INLINE runExceptP_ #-}

-- | Like 'exceptP', but for 'CatchT'.  See 'exceptP' for usage details and
-- caveats.  In general, can be useful for chaining with other 'CatchT'
-- pipes.
--
-- Note that a 'throwM' failure will only ever happen when the input pipe
-- "succesfully" terminates with 'Left'.  It would never happen before the
-- pipe terminates, since you don't get the @'Either' 'SomeException' a@
-- until the pipe succesfully terminates.
--
-- @since 0.2.1.0
catchP
    :: Monad m
    => Pipe i o u m (Either SomeException a)
    -> Pipe i o u (CatchT m) a
catchP :: forall (m :: * -> *) i o u a.
Monad m =>
Pipe i o u m (Either SomeException a) -> Pipe i o u (CatchT m) a
catchP Pipe i o u m (Either SomeException a)
p = forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Pipe i o u m (Either SomeException a)
p forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left  SomeException
e -> forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM SomeException
e
    Right a
x -> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
{-# INLINE catchP #-}

-- | Like 'runExceptP', but for 'CatchT'.  See 'runExceptP' for usage
-- details.  In general, can be useful for "isolating" a 'CatchT' pipe from
-- the rest of its chain.
--
-- @since 0.2.1.0
runCatchP
    :: Monad m
    => Pipe i o u (CatchT m) a
    -> Pipe i o u m (Either SomeException a)
-- runCatchP (Pipe (FT f)) = Pipe $ FT $ \pr bd -> either (pr . Left) pure =<< runCatchT (
--       f (lift . pr . Right)
--         (\c -> lift . bd (either (pr . Left) pure <=< runCatchT . c))
--     )
runCatchP :: forall (m :: * -> *) i o u a.
Monad m =>
Pipe i o u (CatchT m) a -> Pipe i o u m (Either SomeException a)
runCatchP = 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 forall {m :: * -> *} {f :: * -> *} {b}.
(Functor m, Functor f) =>
FreeT f (CatchT m) b -> FreeT f m (Either SomeException b)
go
  where
    go :: FreeT f (CatchT m) b -> FreeT f m (Either SomeException b)
go (FreeT CatchT m (FreeF f b (FreeT f (CatchT m) b))
p) = forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. CatchT m a -> m (Either SomeException a)
runCatchT CatchT m (FreeF f b (FreeT f (CatchT m) b))
p forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&>
      forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (f :: * -> *) a b. a -> FreeF f a b
Pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left)
             (\case Pure b
x -> forall (f :: * -> *) a b. a -> FreeF f a b
Pure (forall a b. b -> Either a b
Right b
x); Free f (FreeT f (CatchT m) b)
l -> forall (f :: * -> *) a b. f b -> FreeF f a b
Free (FreeT f (CatchT m) b -> FreeT f m (Either SomeException b)
go forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (CatchT m) b)
l))
      -- \case
      -- Left  e        -> Pure $ Left e
      -- Right (Pure x) -> Pure $ Right x
      -- Right (Free l) -> Free $ go <$> l
{-# INLINE runCatchP #-}

-- | Turn a "parameterized 'Pipe'" into a 'Pipe' that runs over 'ReaderT',
-- so you can chain it with other 'ReaderT' pipes.
--
-- Essentially, instead of directly providing the @r@ in an @r -> 'Pipe'
-- i o u m a@, the @r@ instead comes from the globally shared environment.
--
-- @since 0.2.1.0
readerP
    :: Monad m
    => (r -> Pipe i o u m a)
    -> Pipe i o u (ReaderT r m) a
readerP :: forall (m :: * -> *) r i o u a.
Monad m =>
(r -> Pipe i o u m a) -> Pipe i o u (ReaderT r m) a
readerP r -> Pipe i o u m a
f = forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall b c a. (b -> c) -> (a -> b) -> a -> c
. r -> Pipe i o u m a
f forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall (m :: * -> *) r. Monad m => ReaderT r m r
ask

-- | Turn a pipe over 'ReaderT' into a directly parameterized pipe.
-- Instead of getting the parameter from the globally shared 'ReaderT'
-- environment, give it directly instead.
--
-- It can be useful to "ignore" a globally shared environment and just give
-- the @r@ directly and immediately.
--
-- @since 0.2.1.0
runReaderP
    :: Monad m
    => r
    -> Pipe i o u (ReaderT r m) a
    -> Pipe i o u m a
runReaderP :: forall (m :: * -> *) r i o u a.
Monad m =>
r -> Pipe i o u (ReaderT r m) a -> Pipe i o u m a
runReaderP r
r = forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe (forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
`runReaderT` r
r)

-- | Turn a pipe returning an @(a, w)@ tuple upon termination into a pipe
-- returning @a@, logging the @w@ in an underlying 'WriterT' context.
--
-- This can be useful for composing your pipe with other 'WriterT' pipes,
-- aggregating all to a common global log.
--
-- However, be aware that this only ever 'tell's when the pipe succesfuly
-- terminates.  It doesn't do "streaming logging" -- it only makes one
-- log payload at the point of succesful termination.  To do streaming
-- logging (logging things as you get them), you should probably just
-- directly use 'WriterT' instead, with 'Data.Conduino.Combinators.repeatM'
-- or 'Data.Conduino.Combinators.iterM' or something similar.
--
-- @since 0.2.1.0
writerP
    :: (Monad m, Monoid w)
    => Pipe i o u m (a, w)
    -> Pipe i o u (WriterT w m) a
writerP :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u m (a, w) -> Pipe i o u (WriterT w m) a
writerP Pipe i o u m (a, w)
p = do
    (a
x, w
w) <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Pipe i o u m (a, w)
p
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
tell w
w)

-- | Turn a 'Pipe' that runs over 'WriterT' into a 'Pipe' that returns the
-- final log when it terminates.
--
-- The main usage of this is to "isolate" the log from other pipes in the
-- same chain.  For example, of @p@, @q@, and @r@ are all pipes under
-- 'WriterT', then:
--
-- @
--     p
--  .| q
--  .| r
-- @
--
-- will all share underlying log, and all logging from any of them will
-- accumulate together in an interleaved way.  It is essentially a global
-- log.
--
-- However, if you use 'runWriterP', you can all have them use different
-- encapsulated logs.
--
-- @
--     void (runWriterP p)
--  .| void (runWriterP q)
--  .| runWriterP r
-- @
--
-- In this case, each of those three chained pipes will use their own
-- internal logs, without sharing.
--
-- This is also useful if you want to chain a pipe over 'WriterT' with
-- pipes that don't use state at all: for example if @a@ and @b@ are
-- "non-logging" pipes (/not/ over 'WriterT'), you can do:
--
-- @
--     a
--  .| void (runWriterP q)
--  .| b
-- @
--
-- And @a@ and @b@ will be none the wiser to the fact that @q@ uses
-- 'WriterT' internally.
--
-- @since 0.2.1.0
runWriterP
    :: (Monad m, Monoid w)
    => Pipe i o u (WriterT w m) a
    -> Pipe i o u m (a, w)
runWriterP :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m (a, w)
runWriterP = 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 (forall {t} {m :: * -> *} {f :: * -> *} {a}.
(Semigroup t, Functor m, Functor f) =>
t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go forall a. Monoid a => a
mempty)
  where
    go :: t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go t
w (FreeT WriterT t m (FreeF f a (FreeT f (WriterT t m) a))
p) = forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT forall a b. (a -> b) -> a -> b
$ forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT WriterT t m (FreeF f a (FreeT f (WriterT t m) a))
p forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(FreeF f a (FreeT f (WriterT t m) a)
r, (t
w forall a. Semigroup a => a -> a -> a
<>)->t
w') ->
      case FreeF f a (FreeT f (WriterT t m) a)
r of
        Pure a
x -> forall (f :: * -> *) a b. a -> FreeF f a b
Pure (a
x, t
w')
        Free f (FreeT f (WriterT t m) a)
l -> forall (f :: * -> *) a b. f b -> FreeF f a b
Free forall a b. (a -> b) -> a -> b
$ t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go t
w' forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (WriterT t m) a)
l
{-# INLINE runWriterP #-}

-- | 'runWriterP', but only returning the final log after succesful
-- termination.
--
-- @since 0.2.1.0
execWriterP
    :: (Monad m, Monoid w)
    => Pipe i o u (WriterT w m) a
    -> Pipe i o u m w
execWriterP :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m w
execWriterP = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m (a, w)
runWriterP
{-# INLINE execWriterP #-}

-- | 'writerP', but for "Control.Monad.Trans.Writer.Strict".
--
-- @since 0.2.1.0
writerPS
    :: (Monad m, Monoid w)
    => Pipe i o u m (a, w)
    -> Pipe i o u (WS.WriterT w m) a
writerPS :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u m (a, w) -> Pipe i o u (WriterT w m) a
writerPS Pipe i o u m (a, w)
p = do
    (a
x, w
w) <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift Pipe i o u m (a, w)
p
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) w. Monad m => w -> WriterT w m ()
WS.tell w
w)

-- | 'runWriterP', but for "Control.Monad.Trans.Writer.Strict".
--
-- @since 0.2.1.0
runWriterPS
    :: (Monad m, Monoid w)
    => Pipe i o u (WS.WriterT w m) a
    -> Pipe i o u m (a, w)
runWriterPS :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m (a, w)
runWriterPS = 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 (forall {t} {m :: * -> *} {f :: * -> *} {a}.
(Semigroup t, Functor m, Functor f) =>
t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go forall a. Monoid a => a
mempty)
  where
    go :: t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go t
w (FreeT WriterT t m (FreeF f a (FreeT f (WriterT t m) a))
p) = forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT forall a b. (a -> b) -> a -> b
$ forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
WS.runWriterT WriterT t m (FreeF f a (FreeT f (WriterT t m) a))
p forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(FreeF f a (FreeT f (WriterT t m) a)
r, (t
w forall a. Semigroup a => a -> a -> a
<>)->t
w') ->
      case FreeF f a (FreeT f (WriterT t m) a)
r of
        Pure a
x -> forall (f :: * -> *) a b. a -> FreeF f a b
Pure (a
x, t
w')
        Free f (FreeT f (WriterT t m) a)
l -> forall (f :: * -> *) a b. f b -> FreeF f a b
Free forall a b. (a -> b) -> a -> b
$ t -> FreeT f (WriterT t m) a -> FreeT f m (a, t)
go t
w' forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (WriterT t m) a)
l
{-# INLINE runWriterPS #-}

-- | 'execWriterP', but for "Control.Monad.Trans.Writer.Strict".
--
-- @since 0.2.1.0
execWriterPS
    :: (Monad m, Monoid w)
    => Pipe i o u (WriterT w m) a
    -> Pipe i o u m w
execWriterPS :: forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m w
execWriterPS = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w i o u a.
(Monad m, Monoid w) =>
Pipe i o u (WriterT w m) a -> Pipe i o u m (a, w)
runWriterP
{-# INLINE execWriterPS #-}

-- | Turn a parameterized, state-transforming, log-producing 'Pipe' into
-- a 'Pipe' over 'RWST', which can be useful for chaining it with other
-- 'RWST' pipes.
--
-- See 'stateP' and 'writerP' for more details on caveats, including:
--
-- *  Logging only happens when the @(a,s,w)@-returning pipe terminates.
--    There is no "streaming logging" --- the resulting @w@ is logged all
--    at once.
-- *  When the @(a,s,w)@-returning pipe terminates, whatever state in the
--    'RWST' is overwritten with the @s@ returned.  If other pipes in the
--    chain modify the @s@, their modifications will be overwritten.
--
-- @since 0.2.1.0
rwsP
    :: (Monad m, Monoid w)
    => (r -> s -> Pipe i o u m (a, s, w))
    -> Pipe i o u (RWST r w s m) a
rwsP :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
(r -> s -> Pipe i o u m (a, s, w)) -> Pipe i o u (RWST r w s m) a
rwsP r -> s -> Pipe i o u m (a, s, w)
f = do
    r
r <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall w (m :: * -> *) r s. (Monoid w, Monad m) => RWST r w s m r
RWS.ask
    s
s <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall w (m :: * -> *) r s. (Monoid w, Monad m) => RWST r w s m s
RWS.get
    (a
x, s
s', w
w) <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (r -> s -> Pipe i o u m (a, s, w)
f r
r s
s)
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) w r s. Monad m => w -> RWST r w s m ()
RWS.tell w
w)
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall w (m :: * -> *) s r.
(Monoid w, Monad m) =>
s -> RWST r w s m ()
RWS.put s
s')

-- | Turn a 'Pipe' that runs over 'RWST' into a state-modifying,
-- environment-using, log-accumulating 'Pipe'.  See 'runStateP',
-- 'runWriterP', and 'runReaderP' for the uses and semantics.
--
-- @since 0.2.1.0
runRWSP
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWST r w s m) a
    -> Pipe i o u m (a, s, w)
runRWSP :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSP r
r = 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 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {t} {m :: * -> *} {f :: * -> *} {t} {a}.
(Semigroup t, Functor m, Functor f) =>
t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go forall a. Monoid a => a
mempty
  where
    go :: t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go t
w t
s (FreeT RWST r t t m (FreeF f a (FreeT f (RWST r t t m) a))
p) = forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT forall a b. (a -> b) -> a -> b
$ forall r w s (m :: * -> *) a.
RWST r w s m a -> r -> s -> m (a, s, w)
runRWST RWST r t t m (FreeF f a (FreeT f (RWST r t t m) a))
p r
r t
s forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(FreeF f a (FreeT f (RWST r t t m) a)
q, t
s', (t
w forall a. Semigroup a => a -> a -> a
<>)->t
w') ->
      case FreeF f a (FreeT f (RWST r t t m) a)
q of
        Pure a
x -> forall (f :: * -> *) a b. a -> FreeF f a b
Pure (a
x, t
s', t
w')
        Free f (FreeT f (RWST r t t m) a)
l -> forall (f :: * -> *) a b. f b -> FreeF f a b
Free forall a b. (a -> b) -> a -> b
$ t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go t
w' t
s' forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (RWST r t t m) a)
l
{-# INLINE runRWSP #-}

-- | 'runRWSP', but ignoring the final state.
--
-- @since 0.2.1.0
evalRWSP
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWST r w s m) a
    -> Pipe i o u m (a, w)
evalRWSP :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, w)
evalRWSP r
r s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(a
x,s
_,w
w) -> (a
x,w
w)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSP r
r s
s
{-# INLINE evalRWSP #-}

-- | 'runRWSP', but ignoring the result value.
--
-- @since 0.2.1.0
execRWSP
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWST r w s m) a
    -> Pipe i o u m (s, w)
execRWSP :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (s, w)
execRWSP r
r s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(a
_,s
s',w
w) -> (s
s',w
w)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSP r
r s
s
{-# INLINE execRWSP #-}

-- | 'rwsP', but for "Control.Monad.Trans.RWS.Strict".
--
-- @since 0.2.1.0
rwsPS
    :: (Monad m, Monoid w)
    => (r -> s -> Pipe i o u m (a, s, w))
    -> Pipe i o u (RWSS.RWST r w s m) a
rwsPS :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
(r -> s -> Pipe i o u m (a, s, w)) -> Pipe i o u (RWST r w s m) a
rwsPS r -> s -> Pipe i o u m (a, s, w)
f = do
    r
r <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall w (m :: * -> *) r s. (Monoid w, Monad m) => RWST r w s m r
RWSS.ask
    s
s <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall w (m :: * -> *) r s. (Monoid w, Monad m) => RWST r w s m s
RWSS.get
    (a
x, s
s', w
w) <- forall (m :: * -> *) (n :: * -> *) i o u a.
(Monad m, Monad n) =>
(forall x. m x -> n x) -> Pipe i o u m a -> Pipe i o u n a
hoistPipe forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (r -> s -> Pipe i o u m (a, s, w)
f r
r s
s)
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall (m :: * -> *) w r s. Monad m => w -> RWST r w s m ()
RWSS.tell w
w)
    a
x forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall w (m :: * -> *) s r.
(Monoid w, Monad m) =>
s -> RWST r w s m ()
RWSS.put s
s')

-- | 'runRWSPS', but for "Control.Monad.Trans.RWS.Strict".
--
-- @since 0.2.1.0
runRWSPS
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWSS.RWST r w s m) a
    -> Pipe i o u m (a, s, w)
runRWSPS :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSPS r
r = 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 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {t} {m :: * -> *} {f :: * -> *} {t} {a}.
(Semigroup t, Functor m, Functor f) =>
t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go forall a. Monoid a => a
mempty
  where
    go :: t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go t
w t
s (FreeT RWST r t t m (FreeF f a (FreeT f (RWST r t t m) a))
p) = forall (f :: * -> *) (m :: * -> *) a.
m (FreeF f a (FreeT f m a)) -> FreeT f m a
FreeT forall a b. (a -> b) -> a -> b
$ forall r w s (m :: * -> *) a.
RWST r w s m a -> r -> s -> m (a, s, w)
RWSS.runRWST RWST r t t m (FreeF f a (FreeT f (RWST r t t m) a))
p r
r t
s forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
<&> \(FreeF f a (FreeT f (RWST r t t m) a)
q, t
s', (t
w forall a. Semigroup a => a -> a -> a
<>)->t
w') ->
      case FreeF f a (FreeT f (RWST r t t m) a)
q of
        Pure a
x -> forall (f :: * -> *) a b. a -> FreeF f a b
Pure (a
x, t
s', t
w')
        Free f (FreeT f (RWST r t t m) a)
l -> forall (f :: * -> *) a b. f b -> FreeF f a b
Free forall a b. (a -> b) -> a -> b
$ t -> t -> FreeT f (RWST r t t m) a -> FreeT f m (a, t, t)
go t
w' t
s' forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> f (FreeT f (RWST r t t m) a)
l
{-# INLINE runRWSPS #-}

-- | 'evalRWSPS', but for "Control.Monad.Trans.RWS.Strict".
--
-- @since 0.2.1.0
evalRWSPS
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWSS.RWST r w s m) a
    -> Pipe i o u m (a, w)
evalRWSPS :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, w)
evalRWSPS r
r s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(a
x,s
_,w
w) -> (a
x,w
w)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSPS r
r s
s
{-# INLINE evalRWSPS #-}

-- | 'execRWSPS', but for "Control.Monad.Trans.RWS.Strict".
--
-- @since 0.2.1.0
execRWSPS
    :: (Monad m, Monoid w)
    => r
    -> s
    -> Pipe i o u (RWSS.RWST r w s m) a
    -> Pipe i o u m (s, w)
execRWSPS :: forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (s, w)
execRWSPS r
r s
s = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(a
_,s
s',w
w) -> (s
s',w
w)) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) w r s i o u a.
(Monad m, Monoid w) =>
r -> s -> Pipe i o u (RWST r w s m) a -> Pipe i o u m (a, s, w)
runRWSPS r
r s
s
{-# INLINE execRWSPS #-}