diff --git a/Control/Proxy/Parse.hs b/Control/Proxy/Parse.hs
deleted file mode 100644
--- a/Control/Proxy/Parse.hs
+++ /dev/null
@@ -1,311 +0,0 @@
--- | Parsing utilities for pipes
-
-module Control.Proxy.Parse (
-    -- * Pushback and Leftovers
-    -- $pushback
-    draw,
-    unDraw,
-
-    -- * Utilities
-    peek,
-    isEndOfInput,
-    drawAll,
-    skipAll,
-    passUpTo,
-    passWhile,
-
-    -- * Adapters
-    -- $adapters
-    wrap,
-    unwrap,
-    fmapPull,
-    returnPull,
-    bindPull,
-
-    -- * Lenses
-    -- $lenses
-    zoom,
-    _fst,
-    _snd,
-
-    -- * Re-exports
-    -- $reexports
-    module Control.Proxy.Trans.State,
-    module Data.Monoid
-    ) where
-
-import Control.Monad (forever)
-import Control.Proxy ((>->), (\>\), (//>), (>\\), (?>=))
-import qualified Control.Proxy as P
-import Control.Proxy.Trans.State (
-    StateP(StateP, unStateP),
-    state,
-    stateT,
-    runStateP,
-    runStateK,
-    evalStateP,
-    evalStateK,
-    execStateP,
-    execStateK,
-    get,
-    put,
-    modify,
-    gets )
-import Data.Monoid (Monoid(mempty, mappend))
-
-{- $pushback
-    'unDraw' stores all leftovers in a 'StateP' buffer and 'draw' retrieves
-    leftovers from this buffer before drawing new input from upstream.
--}
-
-{-| Like @request ()@, except try to use the leftovers buffer first
-
-    A 'Nothing' return value indicates end of input.
--}
-draw :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)
-draw = do
-    s <- get
-    case s of
-        []   -> P.request ()
-        a:as -> do
-            put as
-            return (Just a)
-{-# INLINABLE draw #-}
-
--- | Push an element back onto the leftovers buffer
-unDraw :: (Monad m, P.Proxy p) => a -> StateP [a] p x' x y' y m ()
-unDraw a = modify (a:)
-{-# INLINABLE unDraw #-}
-
--- | Peek at the next element without consuming it
-peek :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)
-peek = do
-    ma <- draw
-    case ma of
-        Nothing -> return ()
-        Just a  -> unDraw a
-    return ma
-{-# INLINABLE peek #-}
-
--- | Check if at end of input stream.
-isEndOfInput :: (Monad m, P.Proxy p) => StateP [a] p () (Maybe a) y' y m Bool
-isEndOfInput = do
-    ma <- peek
-    case ma of
-        Nothing -> return True
-        Just _  -> return False
-{-# INLINABLE isEndOfInput #-}
-
-{-| Fold all input into a list
-
-    Note: 'drawAll' is usually an anti-pattern.
--}
-drawAll :: (Monad m, P.Proxy p) => () -> StateP [a] p () (Maybe a) y' y m [a]
-drawAll = \() -> go id
-  where
-    go diffAs = do
-        ma <- draw
-        case ma of
-            Nothing -> return (diffAs [])
-            Just a  -> go (diffAs . (a:))
-{-# INLINABLE drawAll #-}
-
--- | Consume the input completely, discarding all values
-skipAll :: (Monad m, P.Proxy p) => () -> StateP [a] p () (Maybe a) y' y m ()
-skipAll = \() -> go
-  where
-    go = do
-        ma <- draw
-        case ma of
-            Nothing -> return ()
-            Just _  -> go
-{-# INLINABLE skipAll #-}
-
--- | Forward up to the specified number of elements downstream
-passUpTo
-    :: (Monad m, P.Proxy p)
-    => Int -> () -> P.Pipe (StateP [a] p) (Maybe a) (Maybe a) m r
-passUpTo n0 = \() -> go n0
-  where
-    go n0 =
-        if (n0 <= 0)
-        then forever $ P.respond Nothing
-        else do
-            ma <- draw
-            P.respond ma
-            case ma of
-                Nothing -> forever $ P.respond Nothing
-                Just _  -> go (n0 - 1)
-{-# INLINABLE passUpTo #-}
-
-{-| Forward downstream as many consecutive elements satisfying a predicate as
-    possible
--}
-passWhile
-    :: (Monad m, P.Proxy p)
-    => (a -> Bool) -> () -> P.Pipe (StateP [a] p) (Maybe a) (Maybe a) m r
-passWhile pred = \() -> go
-  where
-    go = do
-        ma <- draw
-        case ma of
-            Nothing -> forever $ P.respond Nothing
-            Just a  ->
-                if (pred a)
-                then do
-                    P.respond ma
-                    go
-                else do
-                    unDraw a
-                    forever $ P.respond Nothing
-{-# INLINABLE passWhile #-}
-
-{- $adapters
-    Use 'wrap' and 'unwrap' to convert between guarded and unguarded pipes.
-
-    'fmapPull', 'returnPull', and 'bindPull' promote compatibility with
-    existing utilities that are not 'Maybe'-aware.
--}
-
-{-| Guard a pipe from terminating by wrapping every output in 'Just' and ending
-    with a never-ending stream of 'Nothing's.
--}
-wrap :: (Monad m, P.Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s
-wrap = \p -> P.runIdentityP $ do
-    P.IdentityP p //> \b -> P.respond (Just b)
-    forever $ P.respond Nothing
-{-# INLINABLE wrap #-}
-
-{-| Compose 'unwrap' downstream of a guarded pipe to unwrap all 'Just's and
-    terminate on the first 'Nothing'.
--}
-unwrap :: (Monad m, P.Proxy p) => x -> p x (Maybe a) x a m ()
-unwrap = \x -> P.runIdentityP (go x)
-  where
-    go x = do
-        ma <- P.request x
-        case ma of
-            Nothing -> return ()
-            Just a  -> do
-                x2 <- P.respond a
-                go x2
-{-# INLINABLE unwrap #-}
-
-{-| Lift a 'Maybe'-oblivious pipe to a 'Maybe'-aware pipe by auto-forwarding
-    all 'Nothing's.
-
-> fmapPull f >-> fmapPull g = fmapPull (f >-> g)
->
-> fmapPull pull = pull
--}
-fmapPull
-    :: (Monad m, P.Proxy p)
-    => (x -> p x        a  x        b  m r)
-    -> (x -> p x (Maybe a) x (Maybe b) m r)
-fmapPull f = bindPull (f >-> returnPull)
-{-# INLINABLE fmapPull #-}
-
--- | Wrap all values flowing downstream in 'Just'.
-returnPull :: (Monad m, P.Proxy p) => x -> p x a x (Maybe a) m r
-returnPull = P.mapD Just
-{-# INLINABLE returnPull #-}
-
-{-| Lift a 'Maybe'-generating pipe to a 'Maybe'-transforming pipe by
-    auto-forwarding all 'Nothing's
-
-> -- Using: f >>> g = f >-> bindPull g
->
-> returnPull >>> f = f
->
-> f >>> returnPull = f
->
-> (f >>> g) >>> h = f >>> (g >>> h)
-
-Or equivalently:
-
-> returnPull >-> bindPull f = f
->
-> bindPull returnPull = pull
->
-> bindPull (f >-> bindPull g) = bindPull f >-> bindPull g
--}
-bindPull
-    :: (Monad m, P.Proxy p)
-    => (x -> p x        a  x (Maybe b) m r)
-    -> (x -> p x (Maybe a) x (Maybe b) m r)
-bindPull f = P.runIdentityP . (up \>\ P.IdentityP . f)
-  where
-    up a' = do
-        ma <- P.request a'
-        case ma of
-            Nothing -> do
-                a'2 <- P.respond Nothing
-                up a'2
-            Just a  -> return a
-{-# INLINABLE bindPull #-}
-
-{- $lenses
-    Use 'zoom', '_fst', and '_snd' to mix pipes that have different leftover
-    buffers or to isolate leftover buffers of different parsing stages.
--}
-
-{-| 'zoom' in on a sub-state using a @Lens'@.
-
-> zoom :: Lens' s1 s2 -> StateP s2 p a' a b' b m r -> StateP s1 p a' a b' b m r
-
-> zoom (f . g) = zoom f . zoom g
->
-> zoom id = id
--}
-zoom
-    :: (Monad m, P.Proxy p)
-    => ((s2 -> (s2, s2)) -> (s1 -> (s2, s1)))
-    -- ^ @Lens'@ s1 s2
-    -> StateP s2 p a' a b' b m r
-    -- ^ Local state
-    -> StateP s1 p a' a b' b m r
-    -- ^ Global state
-zoom lens = \p -> StateP $ \s2_0 ->
-    let (s1_0, s2_0') = lens (\x -> (x, x)) s2_0
-    in  (up >\\ P.thread_P (unStateP p s1_0) s2_0' //> dn) ?>= nx
-  where
-    up ((a', s1), s2) =
-        let (_, s2') = lens (\x -> (x, s1)) s2
-        in  P.request (a', s2') ?>= \(a, s2'') ->
-            let (s1', s2''') = lens (\x -> (x, x)) s2''
-            in  P.return_P ((a, s1'), s2''')
-    dn ((b, s1), s2) =
-        let (_, s2') = lens (\x -> (x, s1)) s2
-        in  P.respond (b, s2') ?>= \(b', s2'') ->
-            let (s1', s2''') = lens (\x -> (x, x)) s2''
-            in  P.return_P ((b', s1'), s2''')
-    nx ((r, s1), s2) =
-        let (_, s2') = lens (\x -> (x, s1)) s2
-        in  P.return_P (r, s2')
-{-# INLINABLE zoom #-}
-
-{-| A @Lens'@ to the first element of a pair.
-
-    Like @_1@, but more monomorphic
-
-> _fst :: Lens' (a, b) a
--}
-_fst :: (Functor f) => (a -> f b) -> ((a, x) -> f (b, x))
-_fst = \f (a, x) -> fmap (\b -> (b, x)) (f a)
-{-# INLINABLE _fst #-}
-
-{-| A @Lens'@ to the second element of a pair.
-
-    Like @_2@, but more monomorphic
-
-> _snd :: Lens' (a, b) b
--}
-_snd :: (Functor f) => (a -> f b) -> ((x, a) -> f (x, b))
-_snd = \f (x, a) -> fmap (\b -> (x, b)) (f a)
-{-# INLINABLE _snd #-}
-
-{- $reexports
-    "Control.Proxy.Trans.State" re-exports all functions.
-
-    "Data.Monoid" re-exports the 'Monoid' class.
--}
diff --git a/Control/Proxy/Parse/Tutorial.hs b/Control/Proxy/Parse/Tutorial.hs
deleted file mode 100644
--- a/Control/Proxy/Parse/Tutorial.hs
+++ /dev/null
@@ -1,463 +0,0 @@
-{-| This module provides the tutorial for the @pipes-parse@ library
-
-    This tutorial assumes that you have read the @pipes@ tutorial in
-    @Control.Proxy.Tutorial@.
--}
-
-module Control.Proxy.Parse.Tutorial (
-    -- * Introduction
-    -- $introduction
-
-    -- * End of input
-    -- $eof
-
-    -- * Compatibility
-    -- $compatibility
-
-    -- * Pushback and leftovers
-    -- $leftovers
-
-    -- * Diverse leftovers
-    -- $diverse
-
-    -- * Isolating leftovers
-    -- $mix
-
-    -- * Return value
-    -- $return
-
-    -- * Resumable Parsing
-    -- $resume
-
-    -- * Nesting
-    -- $nesting
-
-    -- * Conclusion
-    -- $conclusion
-    ) where
-
-import Control.Proxy
-import Control.Proxy.Parse
-
-{- $introduction
-    @pipes-parse@ provides utilities commonly required for parsing streams using
-    @pipes@:
-
-    * End of input utilities and conventions for the @pipes@ ecosystem
-
-    * Pushback and leftovers support for saving unused input
-
-    * Tools to combine parsing stages with diverse or isolated leftover buffers
-
-    * Ways to delimit parsers to subsets of streams
-
-    Use these utilities to parse and validate streaming input in constant
-    memory.
--}
-
-{- $eof
-    To guard an input stream against termination, protect it with the 'wrap'
-    function:
-
-> wrap :: (Monad m, Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s
-
-    This wraps all output values in a 'Just' and then protects against
-    termination by producing a never-ending stream of 'Nothing' values:
-
->>> -- Before
->>> runProxy $ enumFromToS 1 3 >-> printD
-1
-2
-3
->>> -- After
->>> runProxy $ wrap . enumFromToS 1 3 >-> printD
-Just 1
-Just 2
-Just 3
-Nothing
-Nothing
-Nothing
-Nothing
-...
-
-    You can also 'unwrap' streams:
-
-> unwrap :: (Monad m, Proxy p) => x -> p x (Maybe a) x a m ()
-
-    'unwrap' behaves like the inverse of 'wrap'.  Compose 'unwrap' downstream of
-    a pipe to unwrap every 'Just' and terminate on the first 'Nothing':
-
-> wrap . p >-> unwrap = p
-
-    You will commonly use 'unwrap' to terminate an infinite stream:
-
->>> runProxy $ wrap . enumFromToS 1 3 >-> printD >-> unwrap
-Just 1
-Just 2
-Just 3
-Nothing
-
--}
-
-{- $compatibility
-    What if we want to ignore the 'Maybe' machinery entirely and interact with
-    the original unwrapped stream?  We can use 'fmapPull' to lift existing
-    proxies to ignore all 'Nothing's and only operate on the 'Just's:
-
-> fmapPull
->     :: (Monad m, Proxy p)
->     => (x -> p x        a  x        b  m r)
->     -> (x -> p x (Maybe a) x (Maybe b) m r)
-
-    We can use this to lift 'printD' to operate on the original stream:
-
->>> runProxy $ wrap . enumFromToS 1 3 >-> fmapPull printD >-> unwrap
-1
-2
-3
-
-    This lifting cleanly distributes over composition and obeys the following
-    laws:
-
-> fmapPull (f >-> g) = fmapPull f >-> fmapPull g
->
-> fmapPull pull = pull
-
-    You can navigate even more complicated mixtures of 'Maybe'-aware and
-    'Maybe'-oblivious code using 'bindPull' and 'returnPull'.
-
-    @pipes-parse@ requires no buy-in from the rest of the @pipes@ ecosystem
-    thanks to these adapter routines that automatically lift existing pipes to
-    interoperate with end-of-input protocols.
--}
-
-{- $leftovers
-    To take advantage of leftovers support, just replace your 'request's with
-    'draw':
-
-> draw :: (Monad m, Proxy p) => StateP [a] p () (Maybe a) y' y m (Maybe a)
-
-    ... and use 'unDraw' to push back leftovers:
-
-> unDraw :: (Monad m, Proxy p) => a -> StateP [a] p x' x y' y m ()
-
-    These both use a last-in-first-out (LIFO) leftovers buffer of type @[a]@
-    stored in a 'StateP' layer.  'unDraw' prepends elements to this list of
-    leftovers and 'draw' will consume elements from the head of the leftovers
-    list until it is empty before requesting new input from upstream:
-
-> consumer :: (Proxy p) => () -> Consumer (StateP [a] p) (Maybe Int) IO ()
-> consumer () = do
->     ma <- draw
->     lift $ print ma
->     -- You can push back values you never drew
->     unDraw 99
->     -- You can push back more than one value at a time
->     case ma of
->         Nothing -> return ()
->         -- The leftovers buffer only stores unwrapped values
->         Just a  -> unDraw a
->     -- Values come out of the buffer in last-in-first-out (LIFO) order
->     replicateM_ 2 $ do
->         ma <- draw
->         lift $ print ma
-
-    To run the 'StateP' layer, just provide an empty initial state using
-    'mempty':
-
->>> runProxy $ evalStateK mempty $ wrap . enumFromS 1 >-> consumer
-Just 1
-Just 1
-Just 99
-
--}
-
-{- $diverse
-    Why use 'mempty' instead of @[]@?  @pipes-parse@ lets you easily mix
-    distinct leftovers buffers into the same 'StateP' layer and 'mempty' will
-    still do the correct thing when you use multiple buffers.
-
-    For example, suppose that we need to compose parsing pipes that have
-    different input types and therefore different types of leftovers buffers,
-    such as the following two parsers:
-
-> tallyLength
->     :: (Monad m, Proxy p)
->     => () -> Pipe (StateP [String] p) (Maybe String) (Maybe Int) m r
-> tallyLength () = loop 0
->   where
->     loop tally = do
->         respond (Just tally)
->         mstr <- draw
->         case mstr of
->             Nothing  -> forever $ respond Nothing
->             Just str -> loop (tally + length str)
->
-> adder
->     :: (Monad m, Proxy p)
->     => () -> Consumer (StateP [Int] p) (Maybe Int) m Int
-> adder () = fmap sum $ drawAll ()
-
-    We can use 'zoom' to unify these two parsers to share the same 'StateP'
-    layer:
-
-> combined
->     :: (Monad m, Proxy p)
->     => () -> Consumer (StateP ([String], [Int]) p) (Maybe String) m Int
-> --                                 ^       ^
-> --                                 |       |
-> --        Two leftovers buffers ---+-------+
-> combined = zoom _fst . tallyLength >-> zoom _snd . adder
->
-> source :: (Monad m, Proxy p) => () -> Producer p String m ()
-> source = fromListS ["One", "Two", "Three"]
-
-    'zoom' takes a @Lens'@ as an argument which specifies which subset of the
-    state that each parser will use.  '_fst' directs the @tallyLength@ parser to
-    use the @[String]@ leftovers buffer and '_snd' directs the @adder@ parser to
-    use the @[Int]@ leftovers buffer.
-
-    Notice that we can still run the mixture of buffers by supplying 'mempty':
-
->>> runProxy $ evalStateK mempty $ wrap . source >-> combined
-20
-
-    This works because:
-
-> (mempty :: ([String], [Int])) = ([], [])
-
-    Let's study the type of 'zoom' to understand how it works:
-
-> -- zoom's true type is slightly different to avoid a dependency on `lens`
-> zoom :: Lens' s1 s2 -> StateP s2 p a' a b' b m r -> StateP s1 p a' a b' b m r
-
-    'zoom' behaves like the function of the same name from the @lens@ package
-    and zooms in on a sub-state using the provided lens.  When we give it the
-    '_fst' lens we zoom in on the first element of a tuple:
-
-> _fst :: Lens' (s1, s2) s1
->
-> zoom _fst :: StateP s1 p a' a b' b m r -> StateP (s1, s2) p a' a b' b m r
-
-    ... and when we give it the '_snd' lens we zoom in on the second element of
-    a tuple:
-
-> _snd :: Lens' (s1, s2) s2
->
-> zoom _snd :: StateP s2 p a' a b' b m r -> StateP (s1, s2) p a' a b' b m r
-
-    '_fst' and '_snd' are like '_1' and '_2' from the @lens@ package, except
-    with a more monomorphic type.  This ensures that type inference works
-    correctly when supplying 'mempty' as the initial state.
-
-    If you want to merge more than one leftovers buffer, you can either nest
-    pairs of tuples:
-
-> p = zoom _fst . p1 >-> zoom (_snd . _fst) . p2 >-> zoom (_snd . _snd) . p3
-
-    ... or you can create a data type that holds all your leftovers and generate
-    lenses to its fields:
-
-> import Control.Lens hiding (zoom)
->
-> data Leftovers = Leftovers
->     { _buf1 :: [String]
->     , _buf2 :: [Int]
->     , _buf3 :: [Double]
->     }
-> makeLenses ''Leftovers
-> -- Generates:
-> -- buf1 :: Lens' Leftovers [String]
-> -- buf2 :: Lens' Leftovers [Int]
-> -- buf3 :: Lens' Leftovers [Double]
->
-> instance Monoid Leftovers where
->     mempty = Leftovers [] [] []
->     mappend (Leftovers as bs cs) (Leftovers as' bs' cs')
->         = Leftovers (as ++ as') (bs ++ bs') (cs ++ cs')
->
-> p = zoom buf1 . p1 >-> zoom buf2 . p2 >-> zoom buf3 . p3
-
-    'zoom' works seamlessly with all lenses from the @lens@ package, but you
-    don't need a @lens@ dependency to use @pipes-parse@.
--}
-
-{- $mix
-    'zoom' isn't the only way to isolate buffers.  Let's say that you want to
-    mix the following three @pipes-parse@ utilities:
-
-> -- Transmit up to the specified number of elements
-> passUpTo
->     :: (Monad m, Proxy p)
->     => Int -> () -> Pipe (StateP [a] p) (Maybe a) (Maybe a) m r
->
-> -- Fold all input into a list
-> drawAll :: (Monad m, Proxy p) => () -> StateP [a] p () (Maybe a) y' y m [a]
->
-> -- Check if at end of input stream
-> isEndOfInput :: (Monad m, Proxy p) => StateP [a] p () (Maybe a) y' y m Bool
-
-    We might expect the following code to yield chunks of three elements at a
-    time:
-
-> chunks :: (Monad m, Proxy p) => () -> Pipe (StateP [a] p) (Maybe a) [a] m ()
-> chunks () = loop
->   where
->     loop = do
->         as <- (passUpTo 3 >-> drawAll) ()
->         respond as
->         eof <- isEndOfInput
->         unless eof loop
-
-    ... but it doesn't:
-
->>> runProxy $ evalStateK mempty $ wrap . enumFromToS 1 15 >-> chunks >-> printD
-[1,2,3]
-[4,5,6,7]
-[8,9,10,11]
-[12,13,14,15]
-
-    @chunks@ behaves strangely because 'drawAll' shares the same leftovers
-    buffer as 'passUpTo' and 'isEndOfInput'.  After the first chunk completes,
-    'isEndOfInput' peeks at the next value, @4@, and immediately 'unDraw's the
-    value.  'drawAll' retrieves this undrawn value from the leftovers before
-    consulting 'passUpTo' which is why every subsequent list contains an extra
-    element.
-
-    We often don't want composed parsing stages like 'drawAll' to share the same
-    leftovers buffer as upstream stages, but we also don't want to use 'zoom' to
-    add yet another permanent buffer to our global leftovers state.  To solve
-    this, we embed 'drawAll' within a transient 'StateP' layer using
-    'evalStateK':
-
-> chunks () = loop
->   where
->     loop = do
->         as  <- (passUpTo 3 >-> evalStateK mempty drawAll) ()
->         respond as
->         eof <- isEndOfInput
->         unless eof loop
-
-    This runs 'drawAll' within a fresh temporary buffer so that it does not
-    reuse the same buffer as the surrounding pipe:
-
->>> runProxy $ evalStateK mempty $ wrap . enumFromToS 1 15 >-> chunks >-> printD
-[1,2,3]
-[4,5,6]
-[7,8,9]
-[10,11,12]
-[13,14,15]
-
-    Conversely, remove the 'evalStateK' if you deliberately want downstream
-    parsers to share the same leftovers buffers.
--}
-
-{- $return
-    'wrap' allows you to return values directly from parsers because it produces
-    a polymorphic return value:
-
-> -- The 's' is polymorphic and will type-check as anything
-> wrap :: (Monad m, Proxy p) => p a' a b' b m r -> p a' a b' (Maybe b) m s
-
-    This means that if you compose a parser downstream the parser can return the
-    result directly:
-
-> parser
->     :: (Monad m, Proxy p)
->     => () -> Consumer (StateP [a] p) (Maybe a) m (Maybe a, Maybe a)
-> parser () = do
->     mx <- draw
->     my <- draw
->     return (mx, my)  -- Return the result
-
-    The polymorphic return value of 'wrap' will type-check as anything,
-    including our parser's result:
-
-> session
->     :: (Monad m, Proxy p)
->     => () -> Session (StateP [Int] p) m (Maybe Int, Maybe Int)
-> session = wrap . enumFromToS 0 9 >-> parser
-
-    So we can run this 'Session' and retrieve the result directly from the
-    return value:
-
->>> runProxy $ evalStateK session
-(Just 0, Just 1)
-
--}
-
-{- $resume
-    You can save leftovers buffers if you need to interrupt parsing for any
-    reason.  Just replace 'evalStateK' with 'runStateK':
-
->>> let session = wrap . enumFromS 0 >-> passWhile (< 3) >-> printD >-> unwrap
->>> runProxy $ runStateK mempty session
-Just 0
-Just 1
-Just 2
-Nothing
-((), [3])
-
-    This returns the leftovers buffers in the result so that you can reuse them
-    later on.  In the above example, 'passWhile' pushed back the @3@ input onto
-    the leftovers buffer, so the result includes the unused @3@.
--}
-
-{- $nesting
-    @pipes-parse@ allows you to cleanly delimit the scope of sub-parsers by
-    restricting them to a subset of the stream, as the following example
-    illustrates:
-
-> import Control.Proxy
-> import Control.Proxy.Parse
->
-> parser
->     :: (Proxy p)
->     => () -> Consumer (StateP [Int] p) (Maybe Int) IO ([Int], [Int])
-> parser () = do
->     lift $ putStrLn "Skip the first three elements"
->     (passUpTo 3 >-> evalStateK mempty skipAll) ()
->     lift $ putStrLn "Restrict subParser to consecutive elements less than 10"
->     (passWhile (< 10) >-> evalStateK mempty subParser) ()
->
-> subParser
->     :: (Proxy p)
->     => () -> Consumer (StateP [Int] p) (Maybe Int) IO ([Int], [Int])
-> subParser () = do
->     lift $ putStrLn "- Get the next four elements"
->     xs <- (passUpTo 4 >-> evalStateK mempty drawAll) ()
->     lift $ putStrLn "- Get the rest of the input"
->     ys <- drawAll ()
->     return (xs, ys)
-
-    Notice how we use 'evalStateK' each time we subset a parser so that the
-    sub-parser uses a fresh and transient leftovers buffer.
-
->>> runProxy $ evalStateK mempty $ wrap . enumFromS 0 >-> parser
-Skip the first three elements
-Restrict subParser to consecutive elements less than 10
-- Get the next four elements
-- Get the rest of the input
-([3,4,5,6],[7,8,9])
-
--}
-
-{- $conclusion
-    @pipes-parse@ provides standardized end-of-input and leftovers utilities for
-    you to use in your @pipes@-based libraries.  Unlike other streaming
-    libraries, you can:
-
-    * mix or isolate leftovers buffers in a precise and type-safe way,
-
-    * easily delimit parsers to subsets of the input, and
-
-    * ignore standardization, thanks to compatibility functions like 'fmapPull'.
-
-    This library is intentionally minimal and datatype-specific parsers belong
-    in derived libraries.  This makes @pipes-parse@ a very light-weight and
-    stable dependency that you can use in your own projects.
-
-    You can ask any questions about @pipes-parse@ and other @pipes@ libraries on
-    the official @pipes@ mailing list at
-    <mailto:haskell-pipes@googlegroups.com>.
--}
diff --git a/pipes-parse.cabal b/pipes-parse.cabal
--- a/pipes-parse.cabal
+++ b/pipes-parse.cabal
@@ -1,5 +1,5 @@
 Name: pipes-parse
-Version: 1.0.0
+Version: 2.0.0
 Cabal-Version: >=1.8.0.2
 Build-Type: Simple
 License: BSD3
@@ -9,30 +9,29 @@
 Maintainer: Gabriel439@gmail.com
 Bug-Reports: https://github.com/Gabriel439/Haskell-Pipes-Parse-Library/issues
 Synopsis: Parsing infrastructure for the pipes ecosystem
-Description: This package defines the generic machinery necessary for common
-    parsing tasks using @pipes@:
-    .
-    * /End of input/: Detect and handle end of input
+Description: @pipes-parse@ builds upon the @pipes@ library to provide shared
+    parsing idioms and utilities:
     .
-    * /Push-back/: Save unused input for later steps
+    * /Perfect Streaming/: Program in a list-like style in constant memory
     .
-    * /Lens Support/: Mix proxies with different leftover buffers using lenses
+    * /Leftovers/: Save unused input for later consumption
     .
-    * /Compatibility/: Transparently upgrade proxies to work with @pipes-parse@
+    * /Connect and Resume/: Use @StateT@ to save unused input for later
     .
-    Import @Control.Proxy.Parse@ to use this library.
+    * /Termination Safety/: Detect and recover from end of input
     .
-    Read @Control.Proxy.Parse.Tutorial@ for an introductory tutorial.
-Category: Control, Pipes, Proxies, Parsing
+    @Pipes.Parse@ contains the full documentation for this library.
+Category: Control, Pipes, Parsing
 Source-Repository head
     Type: git
     Location: https://github.com/Gabriel439/Haskell-Pipes-Parse-Library
 
 Library
+    HS-Source-Dirs: src
     Build-Depends:
-        base  >= 4       && < 5  ,
-        pipes >= 3.3     && < 3.4
-    Exposed-Modules:
-        Control.Proxy.Parse,
-        Control.Proxy.Parse.Tutorial
-    GHC-Options: -O2
+        base         >= 4       && < 5  ,
+        free         >= 3.1     && < 3.5,
+        pipes        >= 4.0     && < 4.1,
+        transformers >= 0.2.0.0 && < 0.4
+    Exposed-Modules: Pipes.Parse
+    GHC-Options: -O2 -Wall
diff --git a/src/Pipes/Parse.hs b/src/Pipes/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Parse.hs
@@ -0,0 +1,350 @@
+{-|
+    Element-agnostic parsing utilities for @pipes@
+
+    @pipes-parse@ provides two ways to parse and transform streams in constant
+    space:
+
+    * The \"list-like\" approach, using the split \/ transform \/ join paradigm
+
+    * The monadic approach, using parser combinators
+
+    The top half of this module provides the list-like approach.  The key idea
+    is that:
+
+> -- '~' means "is analogous to"
+> Producer a m ()            ~   [a]
+>
+> FreeT (Producer a m) m ()  ~  [[a]]
+
+    'FreeT' nests each subsequent 'Producer' within the return value of the
+    previous 'Producer' so that you cannot access the next 'Producer' until you
+    completely drain the current 'Producer'.  However, you rarely need to work
+    with 'FreeT' directly.  Instead, you structure everything using
+    \"splitters\", \"transformations\" and \"joiners\":
+
+> -- A "splitter"
+> Producer a m ()           -> FreeT (Producer a m) m ()  ~   [a]  -> [[a]]
+>
+> -- A "transformation"
+> FreeT (Producer a m) m () -> FreeT (Producer a m) m ()  ~  [[a]] -> [[a]]
+>
+> -- A "joiner"
+> FreeT (Producer a m) m () -> Producer a m ()            ~  [[a]] ->  [a]
+
+    For example, if you wanted to group standard input by equal lines and take
+    the first three groups, you would write:
+
+> import Pipes
+> import qualified Pipes.Parse as Parse
+> import qualified Pipes.Prelude as Prelude
+>
+> threeGroups :: (Monad m, Eq a) => Producer a m () -> Producer a m ()
+> threeGroups = Parse.concat . Parse.takeFree 3 . Parse.groupBy (==)
+> --            ^ Joiner       ^ Transformation   ^ Splitter
+
+    This then limits standard input to the first three consecutive groups of
+    equal lines:
+
+>>> runEffect $ threeGroups Prelude.stdinLn >-> Prelude.stdoutLn
+Group1<Enter>
+Group1
+Group1<Enter>
+Group1
+Group2<Enter>
+Group2
+Group3<Enter>
+Group3
+Group3<Enter>
+Group3
+Group4<Enter>
+>>> -- Done, because we began entering our fourth group
+
+    The advantage of this style or programming is that you never bring more than
+    a single element into memory.  This works because `FreeT` sub-divides the
+    `Producer` without concatenating elements together, preserving the laziness
+    of the underlying 'Producer'.
+
+    The bottom half of this module contains the lower-level monadic parsing
+    primitives.  These are more useful for `pipes` implementers, particularly
+    for building splitters.  I recommend that application developers use the
+    list-like style whenever possible.
+-}
+
+{-# LANGUAGE RankNTypes #-}
+
+module Pipes.Parse (
+    -- * Splitters
+    groupBy,
+    chunksOf,
+    splitOn,
+
+    -- * Transformations
+    takeFree,
+
+    -- * Joiners
+    concat,
+    intercalate,
+
+    -- * Low-level Parsers
+    -- $lowlevel
+    draw,
+    unDraw,
+    peek,
+    isEndOfInput,
+
+    -- * High-level Parsers
+    -- $highlevel
+    input,
+
+    -- * Utilities
+    takeWhile,
+
+    -- * Re-exports
+    -- $reexports
+    module Control.Monad.Trans.Free,
+    module Control.Monad.Trans.State.Strict
+    ) where
+
+import Control.Applicative ((<$>), (<$))
+import qualified Control.Monad.Trans.Free as F
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Free (FreeF(Pure, Free), FreeT(FreeT, runFreeT))
+import qualified Control.Monad.Trans.State.Strict as S
+import Control.Monad.Trans.State.Strict (
+    StateT(StateT, runStateT), evalStateT, execStateT )
+import Pipes (Producer, Pipe, await, yield, next, (>->), Producer')
+import Pipes.Lift (runStateP)
+import qualified Pipes.Prelude as P
+import Prelude hiding (concat, takeWhile)
+
+{-| Split a 'Producer' into a `FreeT`-delimited stream of 'Producer's grouped by
+    the supplied equality predicate
+-}
+groupBy
+    :: (Monad m)
+    => (a -> a -> Bool) -> Producer a m r -> FreeT (Producer a m) m r
+groupBy equal = loop
+  where
+    loop p = do
+        (x, p') <- F.liftF $ runStateP p $ do
+            x <- lift draw
+            case x of
+                Left  r -> return (Just r)
+                Right a -> do
+                    yield a
+                    (Just <$> input) >-> (Nothing <$ takeWhile (equal a))
+        case x of
+            Just r  -> return r
+            Nothing -> loop p'
+{-# INLINABLE groupBy #-}
+
+{-| Split a 'Producer' into a `FreeT`-delimited stream of 'Producer's of the
+    given chunk size
+-}
+chunksOf :: (Monad m) => Int -> Producer a m r -> FreeT (Producer a m) m r
+chunksOf n = loop
+  where
+    loop p = do
+        (x, p') <- F.liftF $ runStateP p $
+            (Just <$> input) >-> (Nothing <$ P.take n)
+        case x of
+            Just r  -> return r
+            Nothing -> loop p'
+{-# INLINABLE chunksOf #-}
+
+{-| Split a 'Producer' into a `FreeT`-delimited stream of 'Producer's separated
+    by elements that satisfy the given predicate
+-}
+splitOn
+    :: (Monad m) => (a -> Bool) -> Producer a m r -> FreeT (Producer a m) m r
+splitOn predicate = loop
+  where
+    loop p = do
+        (x, p') <- F.liftF $ runStateP p $
+            (Just <$> input) >-> (Nothing <$ takeWhile (not . predicate))
+        case x of
+            Just r  -> return r
+            Nothing -> loop p'
+{-# INLINABLE splitOn #-}
+
+-- | Join a 'FreeT'-delimited stream of 'Producer's into a single 'Producer'
+concat :: (Monad m) => FreeT (Producer a m) m r -> Producer a m r
+concat = loop
+  where
+    loop f = do
+        x <- lift (runFreeT f)
+        case x of
+            Pure r -> return r
+            Free p -> do
+                f' <- p
+                concat f'
+{-# INLINABLE concat #-}
+
+{-| Join a 'FreeT'-delimited stream of 'Producer's into a single 'Producer' by
+    intercalating a 'Producer' in between them
+-}
+intercalate
+    :: (Monad m)
+    => Producer a m () -> FreeT (Producer a m) m r -> Producer a m r
+intercalate sep = go0
+  where
+    go0 f = do
+        x <- lift (runFreeT f)
+        case x of
+            Pure r -> return r
+            Free p -> do
+                f' <- p
+                go1 f'
+    go1 f = do
+        x <- lift (runFreeT f)
+        case x of
+            Pure r -> return r
+            Free p -> do
+                sep
+                f' <- p
+                go1 f'
+{-# INLINABLE intercalate #-}
+
+-- | @(take n)@ only keeps the first @n@ functor layers of a 'FreeT'
+takeFree :: (Functor f, Monad m) => Int -> FreeT f m () -> FreeT f m ()
+takeFree = go
+  where
+    go n f =
+        if (n > 0)
+        then FreeT $ do
+            x <- runFreeT f
+            case x of
+                Pure () -> return (Pure ())
+                Free w  -> return (Free (fmap (go $! n - 1) w))
+        else return ()
+{-# INLINABLE takeFree #-}
+
+{- $lowlevel
+    @pipes-parse@ handles end-of-input and pushback by storing a 'Producer' in
+    a 'StateT' layer.
+-}
+
+{-| Draw one element from the underlying 'Producer', returning 'Left' if the
+    'Producer' is empty
+-}
+draw :: (Monad m) => StateT (Producer a m r) m (Either r a)
+draw = do
+    p <- S.get
+    x <- lift (next p)
+    case x of
+        Left   r      -> do
+            S.put (return r)
+            return (Left r)
+        Right (a, p') -> do
+            S.put p'
+            return (Right a)
+{-# INLINABLE draw #-}
+
+-- | Push back an element onto the underlying 'Producer'
+unDraw :: (Monad m) => a -> StateT (Producer a m r) m ()
+unDraw a = S.modify (yield a >>)
+{-# INLINABLE unDraw #-}
+
+{-| 'peek' checks the first element of the stream, but uses 'unDraw' to push the
+    element back so that it is available for the next 'draw' command.
+
+> peek = do
+>     x <- draw
+>     case x of
+>         Left  _ -> return ()
+>         Right a -> unDraw a
+>     return x
+-}
+peek :: (Monad m) => StateT (Producer a m r) m (Either r a)
+peek = do
+    x <- draw
+    case x of
+        Left  _ -> return ()
+        Right a -> unDraw a
+    return x
+{-# INLINABLE peek #-}
+
+{-| Check if the underlying 'Producer' is empty
+
+> isEndOfInput = liftM isLeft peek
+-}
+isEndOfInput :: (Monad m) => StateT (Producer a m r) m Bool
+isEndOfInput = do
+    x <- peek
+    return (case x of
+        Left  _ -> True
+        Right _ -> False )
+{-# INLINABLE isEndOfInput #-}
+
+{- $highlevel
+    'input' provides a 'Producer' that streams from the underlying 'Producer'.
+
+    Streaming from 'input' differs from streaming directly from the underlying
+    'Producer' because any unused input is saved for later, as the following
+    example illustrates:
+
+> import Control.Monad.Trans.State.Strict
+> import Pipes
+> import Pipes.Parse
+> import qualified Pipes.Prelude as P
+>
+> parser :: (Show a) => StateT (Producer a IO ()) IO ()
+> parser = do
+>     runEffect $ input >-> P.take 2 >-> P.show >-> P.stdoutLn
+>
+>     liftIO $ putStrLn "Intermission"
+>
+>     runEffect $ input >-> P.take 2 >-> P.show >-> P.stdoutLn
+
+    The second pipeline resumes where the first pipeline left off:
+
+>>> evalStateT parser (each [1..])
+1
+2
+Intermission
+3
+4
+
+    You can see more examples of how to use these parsing utilities by studying
+    the source code for the above splitters.
+-}
+
+{-| Stream from the underlying 'Producer'
+
+    'input' terminates if the 'Producer' is empty, returning the final return
+    value of the 'Producer'.
+-}
+input :: (Monad m) => Producer' a (StateT (Producer a m r) m) r
+input = loop
+  where
+    loop = do
+        x <- lift draw
+        case x of
+            Left  r -> return r
+            Right a -> do
+                yield a
+                loop
+{-# INLINABLE input #-}
+
+{-| A variation on 'Pipes.Prelude.takeWhile' from @Pipes.Prelude@ that 'unDraw's
+    the first element that does not match
+-}
+takeWhile
+    :: (Monad m) => (a -> Bool) -> Pipe a a (StateT (Producer a m r) m) ()
+takeWhile predicate = loop
+  where
+    loop = do
+        a <- await
+        if (predicate a)
+            then do
+                yield a
+                loop
+            else lift (unDraw a)
+{-# INLINABLE takeWhile #-}
+
+{- $reexports
+    @Control.Monad.Trans.Free@ re-exports 'FreeF', 'FreeT', and 'runFreeT'.
+
+    @Control.Monad.Trans.State.Strict@ re-exports 'StateT', 'runStateT',
+    'evalStateT', and 'execStateT'.
+-}
