{-# LANGUAGE CPP                   #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances  #-}
{-# LANGUAGE Trustworthy           #-}
module Pipes (
    
      Proxy
    , X
    , Effect
    , Effect'
    , runEffect
    
    
    , Producer
    , Producer'
    , yield
    , for
    , (~>)
    , (<~)
    
    
    , Consumer
    , Consumer'
    , await
    , (>~)
    , (~<)
    
    
    , Pipe
    , cat
    , (>->)
    , (<-<)
    
    , ListT(..)
    , runListT
    , Enumerable(..)
    
    , next
    , each
    , every
    , discard
    
    
    , module Control.Monad
    , module Control.Monad.IO.Class
    , module Control.Monad.Trans.Class
    , module Control.Monad.Morph
    , Foldable
    ) where
import Control.Monad (void, MonadPlus(mzero, mplus))
import Control.Monad.Catch (MonadThrow(..), MonadCatch(..))
import Control.Monad.Except (MonadError(..))
import Control.Monad.Fail (MonadFail(..))
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Reader (MonadReader(..))
import Control.Monad.State (MonadState(..))
import Control.Monad.Trans.Class (MonadTrans(lift))
import Control.Monad.Trans.Except (ExceptT, runExceptT)
import Control.Monad.Trans.Identity (IdentityT(runIdentityT))
import Control.Monad.Trans.Maybe (MaybeT(runMaybeT))
import Control.Monad.Writer (MonadWriter(..))
import Control.Monad.Zip (MonadZip(..))
import Pipes.Core
import Pipes.Internal (Proxy(..))
import qualified Data.Foldable as F
#if MIN_VERSION_base(4,8,0)
import Control.Applicative (Alternative(..))
#else
import Control.Applicative
import Data.Foldable (Foldable)
import Data.Traversable (Traversable(..))
#endif
import Data.Semigroup
import Control.Monad.Morph (MFunctor(hoist), MMonad(embed))
infixl 4 <~
infixr 4 ~>
infixl 5 ~<
infixr 5 >~
infixl 7 >->
infixr 7 <-<
yield :: Functor m => a -> Producer' a m ()
yield = respond
{-# INLINABLE [1] yield #-}
for :: Functor m
    =>       Proxy x' x b' b m a'
    
    -> (b -> Proxy x' x c' c m b')
    
    ->       Proxy x' x c' c m a'
for = (//>)
{-# INLINABLE [0] for #-}
{-# RULES
    "for (for p f) g" forall p f g . for (for p f) g = for p (\a -> for (f a) g)
  ; "for p yield" forall p . for p yield = p
  ; "for (yield x) f" forall x f . for (yield x) f = f x
  ; "for cat f" forall f .
        for cat f =
            let go = do
                    x <- await
                    f x
                    go
            in  go
  ; "f >~ (g >~ p)" forall f g p . f >~ (g >~ p) = (f >~ g) >~ p
  ; "await >~ p" forall p . await >~ p = p
  ; "p >~ await" forall p . p >~ await = p
  ; "m >~ cat" forall m .
        m >~ cat =
            let go = do
                    x <- m
                    yield x
                    go
            in  go
  ; "p1 >-> (p2 >-> p3)" forall p1 p2 p3 .
        p1 >-> (p2 >-> p3) = (p1 >-> p2) >-> p3
  ; "p >-> cat" forall p . p >-> cat = p
  ; "cat >-> p" forall p . cat >-> p = p
  #-}
(~>)
    :: Functor 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')
(~>) = (/>/)
{-# INLINABLE (~>) #-}
(<~)
    :: Functor 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')
g <~ f = f ~> g
{-# INLINABLE (<~) #-}
await :: Functor m => Consumer' a m a
await = request ()
{-# INLINABLE [1] await #-}
(>~)
    :: Functor m
    => Proxy a' a y' y m b
    
    -> Proxy () b y' y m c
    
    -> Proxy a' a y' y m c
p1 >~ p2 = (\() -> p1) >\\ p2
{-# INLINABLE [1] (>~) #-}
(~<)
    :: Functor m
    => Proxy () b y' y m c
    
    -> Proxy a' a y' y m b
    
    -> Proxy a' a y' y m c
p2 ~< p1 = p1 >~ p2
{-# INLINABLE (~<) #-}
cat :: Functor m => Pipe a a m r
cat = pull ()
{-# INLINABLE [1] cat #-}
(>->)
    :: Functor m
    => Proxy a' a () b m r
    
    -> Proxy () b c' c m r
    
    -> Proxy a' a c' c m r
p1 >-> p2 = (\() -> p1) +>> p2
{-# INLINABLE [1] (>->) #-}
newtype ListT m a = Select { enumerate :: Producer a m () }
instance Functor m => Functor (ListT m) where
    fmap f p = Select (for (enumerate p) (\a -> yield (f a)))
    {-# INLINE fmap #-}
instance Functor m => Applicative (ListT m) where
    pure a = Select (yield a)
    {-# INLINE pure #-}
    mf <*> mx = Select (
        for (enumerate mf) (\f ->
        for (enumerate mx) (\x ->
        yield (f x) ) ) )
instance Monad m => Monad (ListT m) where
    return   = pure
    {-# INLINE return #-}
    m >>= f  = Select (for (enumerate m) (\a -> enumerate (f a)))
    {-# INLINE (>>=) #-}
#if !MIN_VERSION_base(4,13,0)
    fail _   = mzero
    {-# INLINE fail #-}
#endif
instance Monad m => MonadFail (ListT m) where
    fail _ = mzero
    {-# INLINE fail #-}
instance Foldable m => Foldable (ListT m) where
    foldMap f = go . enumerate
      where
        go p = case p of
            Request v _  -> closed v
            Respond a fu -> f a `mappend` go (fu ())
            M       m    -> F.foldMap go m
            Pure    _    -> mempty
    {-# INLINE foldMap #-}
instance (Functor m, Traversable m) => Traversable (ListT m) where
    traverse k (Select p) = fmap Select (traverse_ p)
      where
        traverse_ (Request v _ ) = closed v
        traverse_ (Respond a fu) = _Respond <$> k a <*> traverse_ (fu ())
          where
            _Respond a_ a' = Respond a_ (\_ -> a')
        traverse_ (M       m   ) = fmap M (traverse traverse_ m)
        traverse_ (Pure     r  ) = pure (Pure r)
instance MonadTrans ListT where
    lift m = Select (do
        a <- lift m
        yield a )
instance (MonadIO m) => MonadIO (ListT m) where
    liftIO m = lift (liftIO m)
    {-# INLINE liftIO #-}
instance (Functor m) => Alternative (ListT m) where
    empty = Select (return ())
    {-# INLINE empty #-}
    p1 <|> p2 = Select (do
        enumerate p1
        enumerate p2 )
instance (Monad m) => MonadPlus (ListT m) where
    mzero = empty
    {-# INLINE mzero #-}
    mplus = (<|>)
    {-# INLINE mplus #-}
instance MFunctor ListT where
    hoist morph = Select . hoist morph . enumerate
    {-# INLINE hoist #-}
instance MMonad ListT where
    embed f (Select p0) = Select (loop p0)
      where
        loop (Request a' fa ) = Request a' (\a  -> loop (fa  a ))
        loop (Respond b  fb') = Respond b  (\b' -> loop (fb' b'))
        loop (M          m  ) = for (enumerate (fmap loop (f m))) id
        loop (Pure    r     ) = Pure r
    {-# INLINE embed #-}
instance (Functor m) => Semigroup (ListT m a) where
    (<>) = (<|>)
    {-# INLINE (<>) #-}
instance (Functor m) => Monoid (ListT m a) where
    mempty = empty
    {-# INLINE mempty #-}
#if !(MIN_VERSION_base(4,11,0))
    mappend = (<|>)
    {-# INLINE mappend #-}
#endif
instance (MonadState s m) => MonadState s (ListT m) where
    get     = lift  get
    {-# INLINE get #-}
    put   s = lift (put   s)
    {-# INLINE put #-}
    state f = lift (state f)
    {-# INLINE state #-}
instance (MonadWriter w m) => MonadWriter w (ListT m) where
    writer = lift . writer
    {-# INLINE writer #-}
    tell w = lift (tell w)
    {-# INLINE tell #-}
    listen l = Select (go (enumerate l) mempty)
      where
        go p w = case p of
            Request a' fa  -> Request a' (\a  -> go (fa  a ) w)
            Respond b  fb' -> Respond (b, w)  (\b' -> go (fb' b') w)
            M          m   -> M (do
                (p', w') <- listen m
                return (go p' $! mappend w w') )
            Pure    r      -> Pure r
    pass l = Select (go (enumerate l) mempty)
      where
        go p w = case p of
            Request  a'     fa  -> Request a' (\a  -> go (fa  a ) w)
            Respond (b, f)  fb' -> M (pass (return
                (Respond b (\b' -> go (fb' b') (f w)), \_ -> f w) ))
            M               m   -> M (do
                (p', w') <- listen m
                return (go p' $! mappend w w') )
            Pure     r          -> Pure r
instance (MonadReader i m) => MonadReader i (ListT m) where
    ask = lift ask
    {-# INLINE ask #-}
    local f l = Select (local f (enumerate l))
    {-# INLINE local #-}
    reader f = lift (reader f)
    {-# INLINE reader #-}
instance (MonadError e m) => MonadError e (ListT m) where
    throwError e = lift (throwError e)
    {-# INLINE throwError #-}
    catchError l k = Select (catchError (enumerate l) (\e -> enumerate (k e)))
    {-# INLINE catchError #-}
instance MonadThrow m => MonadThrow (ListT m) where
    throwM = Select . throwM
    {-# INLINE throwM #-}
instance MonadCatch m => MonadCatch (ListT m) where
    catch l k = Select (Control.Monad.Catch.catch (enumerate l) (\e -> enumerate (k e)))
    {-# INLINE catch #-}
instance Monad m => MonadZip (ListT m) where
    mzipWith f = go
      where
        go xs ys = Select $ do
            xres <- lift $ next (enumerate xs)
            case xres of
                Left r -> return r
                Right (x, xnext) -> do
                    yres <- lift $ next (enumerate ys)
                    case yres of
                        Left r -> return r
                        Right (y, ynext) -> do
                            yield (f x y)
                            enumerate (go (Select xnext) (Select ynext))
runListT :: Monad m => ListT m a -> m ()
runListT l = runEffect (enumerate (l >> mzero))
{-# INLINABLE runListT #-}
class Enumerable t where
    toListT :: Monad m => t m a -> ListT m a
instance Enumerable ListT where
    toListT = id
instance Enumerable IdentityT where
    toListT m = Select $ do
        a <- lift $ runIdentityT m
        yield a
instance Enumerable MaybeT where
    toListT m = Select $ do
        x <- lift $ runMaybeT m
        case x of
            Nothing -> return ()
            Just a  -> yield a
instance Enumerable (ExceptT e) where
    toListT m = Select $ do
        x <- lift $ runExceptT m
        case x of
            Left  _ -> return ()
            Right a -> yield a
next :: Monad m => Producer a m r -> m (Either r (a, Producer a m r))
next = go
  where
    go p = case p of
        Request v _  -> closed v
        Respond a fu -> return (Right (a, fu ()))
        M         m  -> m >>= go
        Pure    r    -> return (Left r)
{-# INLINABLE next #-}
each :: (Functor m, Foldable f) => f a -> Producer' a m ()
each = F.foldr (\a p -> yield a >> p) (return ())
{-# INLINABLE each #-}
every :: (Monad m, Enumerable t) => t m a -> Producer' a m ()
every it = discard >\\ enumerate (toListT it)
{-# INLINABLE every #-}
discard :: Monad m => a -> m ()
discard _ = return ()
{-# INLINABLE discard #-}
(<-<)
    :: Functor m
    => Proxy () b c' c m r
    
    -> Proxy a' a () b m r
    
    -> Proxy a' a c' c m r
p2 <-< p1 = p1 >-> p2
{-# INLINABLE (<-<) #-}