diff --git a/Control/Pipe/Binary.hs b/Control/Pipe/Binary.hs
--- a/Control/Pipe/Binary.hs
+++ b/Control/Pipe/Binary.hs
@@ -15,7 +15,8 @@
   ) where
 
 import Control.Monad
-import Control.Monad.Trans (MonadIO, liftIO, lift)
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Class
 import Control.Pipe
 import Control.Pipe.Exception
 import Control.Pipe.Combinators (tryAwait, feed)
@@ -105,6 +106,7 @@
     go leftover = do
       mchunk <- tryAwait
       case mchunk of
+        Nothing | B.null leftover -> idP
         Nothing -> yield leftover >> idP
         Just chunk -> split chunk leftover
     split chunk leftover
diff --git a/Control/Pipe/ChunkPipe.hs b/Control/Pipe/ChunkPipe.hs
deleted file mode 100644
--- a/Control/Pipe/ChunkPipe.hs
+++ /dev/null
@@ -1,29 +0,0 @@
--- | This module contains utilities to create and combine pipes that accept
--- "chunked" input and return unconsumed portions of their internal buffer.
---
--- The main interface is an alternative monad instance for Pipe, which passes
--- leftover data along automatically.
-module Control.Pipe.ChunkPipe (
-  ChunkPipe(..),
-  nonchunked,
-  ) where
-
-import Control.Pipe
-import Data.Monoid
-
--- | Newtype wrapper for Pipe proving a monad instance that takes care of
--- passing leftover data automatically.
---
--- An individual 'ChunkPipe' is just a regular pipe, but returns unconsumed
--- input in a pair alongside the actual return value.
-newtype ChunkPipe a b m r = ChunkPipe { unChunkPipe :: Pipe a b m (a, r) }
-
-instance (Monoid a, Monad m) => Monad (ChunkPipe a b m) where
-  return = nonchunked . return
-  (ChunkPipe p) >>= f = ChunkPipe $ p >>= \(leftover, result) ->
-    (yield leftover >> idP) >+> unChunkPipe (f result)
-
--- | Create a 'ChunkPipe' out of a regular pipe that is able to consume all its
--- input.
-nonchunked :: (Monoid a, Monad m) => Pipe a b m r -> ChunkPipe a b m r
-nonchunked p = ChunkPipe $ p >>= \r -> return (mempty, r)
diff --git a/Control/Pipe/Coroutine.hs b/Control/Pipe/Coroutine.hs
--- a/Control/Pipe/Coroutine.hs
+++ b/Control/Pipe/Coroutine.hs
@@ -1,46 +1,45 @@
+{-# LANGUAGE DeriveDataTypeable #-}
 module Control.Pipe.Coroutine (
   Coroutine,
-  coroutine,
-  suspend,
-  suspendE,
   resume,
+  suspend,
+  coroutine,
   step,
   terminate
   ) where
 
 import Control.Monad
 import Control.Pipe
+import Control.Pipe.Exception
 import qualified Control.Exception as E
+import Data.Typeable
+import Prelude hiding (catch)
 
 data Coroutine a b m r = Coroutine
-  { suspend :: Pipe a b m r
-  , suspendE :: E.SomeException -> Pipe a b m r }
+  { resume :: Pipe a b m r
+  , finalizer :: [m ()]
+  }
 
-resume :: Monad m
-       => Pipe a b m r
-       -> Pipe a x m (Either r (b, Coroutine a b m r))
-resume (Pure r) = return $ Left r
-resume (Throw e) = throwP e
-resume (Free c h) = go c >>= \x -> case x of
-  Left p       -> resume p
-  Right (b, p) -> return $ Right (b, Coroutine p h)
-  where
-    go (Await k) = liftM (Left . k) await
-    go (Yield b p) = return $ Right (b, p)
-    go (M m s) = liftM Left $ liftP s m
+suspend :: Monad m
+        => Pipe a b m r
+        -> Pipe a x m (Either r (b, Coroutine a b m r))
+suspend (Pure r w) = Pure (Left r) w
+suspend (Throw e w) = Throw e w
+suspend (Yield x p w) = return (Right (x, Coroutine p w))
+suspend (M s m h) = M s (liftM suspend m) (suspend . h)
+suspend (Await k h) = Await (suspend . k) (suspend . h)
 
 coroutine :: Monad m
           => Pipe a b m r
           -> Coroutine a b m r
-coroutine p = Coroutine p throwP
+coroutine p = Coroutine p []
 
 step :: Monad m
      => Coroutine a b m r
      -> Pipe a x m (Either r (b, Coroutine a b m r))
-step = resume . suspend
+step = suspend . resume
 
 terminate :: Monad m
           => Coroutine a b m r
-          -> Pipe a x m ()
-terminate (Coroutine _ h) =
-  void (catchP discard h) >+> return ()
+          -> Pipe a b m ()
+terminate p = mapM_ masked (finalizer p)
diff --git a/Control/Pipe/PutbackPipe.hs b/Control/Pipe/PutbackPipe.hs
new file mode 100644
--- /dev/null
+++ b/Control/Pipe/PutbackPipe.hs
@@ -0,0 +1,73 @@
+-- | This module contains an alternative pipe implementation, 'PutbackPipe',
+-- providing an additional primitive 'putback', which allows data to be
+-- inserted into the input stream of the current pipe.
+--
+-- PutbackPipes can be used to implement pipes with left-over data, and can be
+-- composed vertically (using the Monad instance), but not horizontally.
+--
+-- To make use of a PutbackPipe within a 'Pipeline', you need to convert it to
+-- a regular 'Pipe' using 'runPutback'.
+module Control.Pipe.PutbackPipe (
+  PutbackPipe(..),
+  fromPipe,
+  putback,
+  yield,
+  await,
+  tryAwait,
+  runPutback
+  ) where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Class
+import qualified Control.Pipe as P
+import Control.Pipe ((>+>), Pipe)
+import qualified Control.Pipe.Combinators as PC
+import Control.Pipe.Monoidal
+
+-- | The 'PutbackPipe' data type.
+newtype PutbackPipe a b m r = PutbackPipe {
+  unPutback :: Pipe (Either a a) (Either b a) m r
+  }
+
+instance Monad m => Monad (PutbackPipe a b m) where
+    return = PutbackPipe . return
+    (PutbackPipe p) >>= f = PutbackPipe (p >>= unPutback . f)
+
+instance MonadTrans (PutbackPipe a b) where
+  lift = PutbackPipe . lift
+
+instance Monad m => Functor (PutbackPipe a b m) where
+  fmap f (PutbackPipe p) = PutbackPipe (liftM f p)
+
+instance Monad m => Applicative (PutbackPipe a b m) where
+  pure = return
+  (<*>) = ap
+
+instance MonadIO m => MonadIO (PutbackPipe a b m) where
+  liftIO a = PutbackPipe (liftIO a)
+
+-- | Create a 'PutbackPipe' from a regular pipe.
+fromPipe :: Monad m => Pipe a b m r -> PutbackPipe a b m r
+fromPipe p = PutbackPipe (joinP >+> p >+> P.pipe Left)
+
+-- | Put back an element into the input stream.
+putback :: Monad m => a -> PutbackPipe a b m ()
+putback = PutbackPipe . P.yield . Right
+
+-- | Same as 'P.yield' for regular pipes.
+yield :: Monad m => b -> PutbackPipe a b m ()
+yield = PutbackPipe . P.yield . Left
+
+-- | Same as 'P.await' for regular pipes.
+await :: Monad m => PutbackPipe a b m a
+await = PutbackPipe $ liftM (either id id) P.await
+
+-- | Same as 'PC.tryAwait' for regular pipes.
+tryAwait :: Monad m => PutbackPipe a b m (Maybe a)
+tryAwait = PutbackPipe $ liftM (fmap (either id id)) PC.tryAwait
+
+-- | Convert a 'PutbackPipe' to a regular pipe.
+runPutback :: Monad m => PutbackPipe a b m r -> Pipe a b m r
+runPutback = loopP . unPutback
diff --git a/Control/Pipe/Tee.hs b/Control/Pipe/Tee.hs
--- a/Control/Pipe/Tee.hs
+++ b/Control/Pipe/Tee.hs
@@ -17,10 +17,10 @@
   teeFileBS
   ) where
 
-import Control.Monad.Trans   (MonadIO(..))
+import Control.Monad.IO.Class
 import Control.Pipe
-import Control.Pipe.Binary   (fileWriter)
-import Data.ByteString       (ByteString)
+import Control.Pipe.Binary
+import Data.ByteString
 
 -- | Acts like 'idP', but also passes a copy to the supplied consumer.
 tee :: (Monad m)
diff --git a/Control/Pipe/Zip.hs b/Control/Pipe/Zip.hs
--- a/Control/Pipe/Zip.hs
+++ b/Control/Pipe/Zip.hs
@@ -22,15 +22,15 @@
              => Producer a m r
              -> Pipe (Either () (ProducerControl r)) a m r
 controllable p = do
-  x <- pipe (const ()) >+> resume p
+  x <- pipe (const ()) >+> suspend p
   case x of
     Left r -> return r
     Right (b, p') ->
       join $ onException
         (await >>= \c -> case c of
-          Left () -> yield b >> return (controllable (suspend p'))
+          Left () -> yield b >> return (controllable (resume p'))
           Right (Done r) -> return $ (pipe (const ()) >+> terminate p') >> return r
-          Right (Error e) -> return $ controllable (suspendE p' e))
+          Right (Error e) -> return $ (pipe (const ()) >+> terminate p') >> throw e)
         (pipe (const ()) >+> terminate p')
 
 controllable_ :: Monad m
diff --git a/pipes-extra.cabal b/pipes-extra.cabal
--- a/pipes-extra.cabal
+++ b/pipes-extra.cabal
@@ -1,5 +1,5 @@
 Name: pipes-extra
-Version: 0.0.1
+Version: 0.1.0
 License: BSD3
 License-file: LICENSE
 Author: Paolo Capriotti
@@ -19,12 +19,12 @@
 Library
     Build-Depends:
         base (== 4.*),
-        mtl,
-        pipes-core (== 0.0.*),
+        transformers (>= 0.2 && < 0.4),
+        pipes-core (== 0.1.*),
         bytestring (== 0.9.*)
     Exposed-Modules:
         Control.Pipe.Binary,
         Control.Pipe.Coroutine,
-        Control.Pipe.ChunkPipe,
+        Control.Pipe.PutbackPipe,
         Control.Pipe.Tee,
         Control.Pipe.Zip
