    {-# LANGUAGE
        RankNTypes
      , FlexibleInstances
      , MultiParamTypeClasses
      , UndecidableInstances
      , Trustworthy
      #-}

    {-| This module is the recommended entry point to the @pipes@ library.

        Read "Pipes.Tutorial" if you want a tutorial explaining how to use this
        library.
    -}

    module Pipes (
        -- * The Proxy Monad Transformer
          Proxy
        , X
        , Effect
        , Effect'
        , runEffect

        -- ** Producers
        -- $producers
        , Producer
        , Producer'
        , yield
        , for
        , (~>)
        , (<~)

        -- ** Consumers
        -- $consumers
        , Consumer
        , Consumer'
        , await
        , (>~)
        , (~<)

        -- ** Pipes
        -- $pipes
        , Pipe
        , cat
        , (>->)
        , (<-<)

        -- * ListT
        , ListT(..)
        , Enumerable(..)

        -- * Utilities
        , next
        , each
        , every
        , discard

        -- * Re-exports
        -- $reexports
        , module Control.Monad.IO.Class
        , module Control.Monad.Trans.Class
        , module Control.Monad.Morph
        , module Data.Foldable
        ) where

    import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))
    import Control.Monad.Error (MonadError(..))
    import Control.Monad.IO.Class (MonadIO(liftIO))
    import Control.Monad (MonadPlus(mzero, mplus))
    import Control.Monad.Reader (MonadReader(..))
    import Control.Monad.State (MonadState(..))
    import Control.Monad.Trans.Class (MonadTrans(lift))
    import Control.Monad.Trans.Error (ErrorT(runErrorT))
    import Control.Monad.Trans.Identity (IdentityT(runIdentityT))
    import Control.Monad.Trans.Maybe (MaybeT(runMaybeT))
    import Control.Monad.Writer (MonadWriter(..))
    import Data.Foldable (Foldable)
    import Data.Monoid (Monoid(..))
    import Pipes.Core
    import Pipes.Internal (Proxy(..))
    import qualified Data.Foldable as F

    -- Re-exports
    import Control.Monad.Morph (MFunctor(hoist))

    infixl 4 <~
    infixr 4 ~>
    infixl 5 ~<
    infixr 5 >~
    infixl 7 >->
    infixr 7 <-<

    {- $producers
        Use 'yield' to produce output and ('~>') \/ 'for' to substitute 'yield's.

        'yield' and ('~>') obey the 'Control.Category.Category' laws:

    @
    \-\- Substituting \'yield\' with \'f\' gives \'f\'
    'yield' '~>' f = f

    \-\- Substituting every \'yield\' with another \'yield\' does nothing
    f '~>' 'yield' = f

    \-\- \'yield\' substitution is associative
    (f '~>' g) '~>' h = f '~>' (g '~>' h)
    @

        These are equivalent to the following \"for loop laws\":

    @
    \-\- Looping over a single yield simplifies to function application
    'for' ('yield' x) f = f x

    \-\- Re-yielding every element of a stream returns the original stream
    'for' s 'yield' = s

    \-\- Nested for loops can become a sequential 'for' loops if the inner loop
    \-\- body ignores the outer loop variable
    'for' s (\\a -\> 'for' (f a) g) = 'for' ('for' s f) g = 'for' s (f '~>' g)
    @

    -}

    {-| Produce a value

    @
    'yield' :: 'Monad' m => a -> 'Pipe' x a m ()
    @
    -}
    yield :: Monad m => a -> Producer' a m ()
    yield = respond
    {-# INLINABLE yield #-}

    {-| @(for p body)@ loops over @p@ replacing each 'yield' with @body@.

    @
    'for' :: 'Monad' m => 'Producer' b m r -> (b -> 'Effect'       m ()) -> 'Effect'       m r
    'for' :: 'Monad' m => 'Producer' b m r -> (b -> 'Producer'   c m ()) -> 'Producer'   c m r
    'for' :: 'Monad' m => 'Pipe'   x b m r -> (b -> 'Consumer' x   m ()) -> 'Consumer' x   m r
    'for' :: 'Monad' m => 'Pipe'   x b m r -> (b -> 'Pipe'     x c m ()) -> 'Pipe'     x c m r
    @
    -}
    for :: Monad 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 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

      #-}

    {-| Compose loop bodies

    @
    ('~>') :: 'Monad' m => (a -> 'Producer' b m r) -> (b -> 'Effect'       m ()) -> (a -> 'Effect'       m r)
    ('~>') :: 'Monad' m => (a -> 'Producer' b m r) -> (b -> 'Producer'   c m ()) -> (a -> 'Producer'   c m r)
    ('~>') :: 'Monad' m => (a -> 'Pipe'   x b m r) -> (b -> 'Consumer' x   m ()) -> (a -> 'Consumer' x   m r)
    ('~>') :: 'Monad' m => (a -> 'Pipe'   x b m r) -> (b -> 'Pipe'     x c m ()) -> (a -> 'Pipe'     x c m r)
    @
    -}
    (~>)
        :: 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')
    (~>) = (/>/)
    {-# INLINABLE (~>) #-}

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

    {- $consumers
        Use 'await' to request input and ('>~') to substitute 'await's.

        'await' and ('>~') obey the 'Control.Category.Category' laws:

    @
    \-\- Substituting every \'await\' with another \'await\' does nothing
    'await' '>~' f = f

    \-\- Substituting \'await\' with \'f\' gives \'f\'
    f '>~' 'await' = f

    \-\- \'await\' substitution is associative
    (f '>~' g) '>~' h = f '>~' (g '>~' h)
    @

    -}

    {-| Consume a value

    @
    'await' :: 'Monad' m => 'Pipe' a y m a
    @
    -}
    await :: Monad m => Consumer' a m a
    await = request ()
    {-# INLINABLE await #-}

    {-| @(draw >~ p)@ loops over @p@ replacing each 'await' with @draw@

    @
    ('>~') :: 'Monad' m => 'Effect'       m b -> 'Consumer' b   m c -> 'Effect'       m c
    ('>~') :: 'Monad' m => 'Consumer' a   m b -> 'Consumer' b   m c -> 'Consumer' a   m c
    ('>~') :: 'Monad' m => 'Producer'   y m b -> 'Pipe'     b y m c -> 'Producer'   y m c
    ('>~') :: 'Monad' m => 'Pipe'     a y m b -> 'Pipe'     b y m c -> 'Pipe'     a y m c
    @
    -}
    (>~)
        :: Monad 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 (>~) #-}

    -- | ('>~') with the arguments flipped
    (~<)
        :: Monad 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 (~<) #-}

    {- $pipes
        Use 'await' and 'yield' to build 'Pipe's and ('>->') to connect 'Pipe's.

        'cat' and ('>->') obey the 'Control.Category.Category' laws:

    @
    \-\- Useless use of cat
    'cat' '>->' f = f

    \-\- Redirecting output to cat does nothing
    f '>->' 'cat' = f

    \-\- The pipe operator is associative
    (f '>->' g) '>->' h = f '>->' (g '>->' h)
    @

    -}

    -- | The identity 'Pipe', analogous to the Unix @cat@ program
    cat :: Monad m => Pipe a a m r
    cat = pull ()
    {-# INLINABLE cat #-}

    {-| 'Pipe' composition, analogous to the Unix pipe operator

    @
    ('>->') :: 'Monad' m => 'Producer' b m r -> 'Consumer' b   m r -> 'Effect'       m r
    ('>->') :: 'Monad' m => 'Producer' b m r -> 'Pipe'     b c m r -> 'Producer'   c m r
    ('>->') :: 'Monad' m => 'Pipe'   a b m r -> 'Consumer' b   m r -> 'Consumer' a   m r
    ('>->') :: 'Monad' m => 'Pipe'   a b m r -> 'Pipe'     b c m r -> 'Pipe'     a c m r
    @
    -}
    (>->)
        :: Monad 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 (>->) #-}

    {-| The list monad transformer, which extends a monad with non-determinism

        'return' corresponds to 'yield', yielding a single value

        ('>>=') corresponds to 'for', calling the second computation once for each
        time the first computation 'yield's.
    -}
    newtype ListT m a = Select { enumerate :: Producer a m () }

    instance (Monad m) => Functor (ListT m) where
        fmap f p = Select (for (enumerate p) (\a -> yield (f a)))

    instance (Monad m) => Applicative (ListT m) where
        pure a = Select (yield a)
        mf <*> mx = Select (
            for (enumerate mf) (\f ->
            for (enumerate mx) (\x ->
            yield (f x) ) ) )

    instance (Monad m) => Monad (ListT m) where
        return a = Select (yield a)
        m >>= f  = Select (for (enumerate m) (\a -> enumerate (f a)))
        fail _   = mzero

    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)

    instance (Monad m) => Alternative (ListT m) where
        empty = Select (return ())
        p1 <|> p2 = Select (do
            enumerate p1
            enumerate p2 )

    instance (Monad m) => MonadPlus (ListT m) where
        mzero = empty
        mplus = (<|>)

    instance MFunctor ListT where
        hoist morph = Select . hoist morph . enumerate

    instance (Monad m) => Monoid (ListT m a) where
        mempty = empty
        mappend = (<|>)

    instance (MonadState s m) => MonadState s (ListT m) where
        get     = lift  get

        put   s = lift (put   s)

        state f = lift (state f)

    instance (MonadWriter w m) => MonadWriter w (ListT m) where
        writer = lift . writer

        tell w = lift (tell w)

        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

        local f l = Select (local f (enumerate l))

        reader f = lift (reader f)

    instance (MonadError e m) => MonadError e (ListT m) where
        throwError e = lift (throwError e)

        catchError l k = Select (catchError (enumerate l) (\e -> enumerate (k e)))

    {-| 'Enumerable' generalizes 'Data.Foldable.Foldable', converting effectful
        containers to 'ListT's.
    -}
    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 (ErrorT e) where
        toListT m = Select $ do
            x <- lift $ runErrorT m
            case x of
                Left  _ -> return ()
                Right a -> yield a

    {-| Consume the first value from a 'Producer'

        'next' either fails with a 'Left' if the 'Producer' terminates or succeeds
        with a 'Right' providing the next value and the remainder of the 'Producer'.
    -}
    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 #-}

    -- | Convert a 'F.Foldable' to a 'Producer'
    each :: (Monad m, Foldable f) => f a -> Producer' a m ()
    each = F.foldr (\a p -> yield a >> p) (return ())
    {-# INLINABLE each #-}
    {-  The above code is the same as:

    > each = Data.Foldable.mapM_ yield

        ... except writing it directly in terms of `Data.Foldable.foldr` improves
        build/foldr fusion
    -}

    -- | Convert an 'Enumerable' to a 'Producer'
    every :: (Monad m, Enumerable t) => t m a -> Producer' a m ()
    every it = discard >\\ enumerate (toListT it)
    {-# INLINABLE every #-}

    -- | Discards a value
    discard :: Monad m => a -> m ()
    discard _ = return ()
    {-# INLINABLE discard #-}

    -- | ('>->') with the arguments flipped
    (<-<)
        :: Monad 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 (<-<) #-}

    {- $reexports
        "Control.Monad.IO.Class" re-exports 'MonadIO'.

        "Control.Monad.Trans.Class" re-exports 'MonadTrans'.

        "Control.Monad.Morph" re-exports 'MFunctor'.

        "Data.Foldable" re-exports 'Foldable' (the class name only)
    -}