diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.8.1.2
+version:       0.8.2.0
 synopsis:      Iteratee-based I/O
 description:
   The Iteratee monad provides strict, safe, and functional I/O. In addition
@@ -10,7 +10,7 @@
 license:       BSD3
 license-file:  LICENSE
 homepage:      http://www.tiresiaspress.us/haskell/iteratee
-tested-with:   GHC == 7.0.1, GHC == 6.12.3
+tested-with:   GHC == 7.0.3, GHC == 6.12.3
 stability:     experimental
 
 cabal-version: >= 1.6
@@ -96,6 +96,7 @@
 
   if flag(buildTests)
     build-depends:
+      mtl                        >= 2   && < 3,
       QuickCheck                 >= 2   && < 3,
       test-framework             >= 0.3 && < 0.4,
       test-framework-quickcheck2 >= 0.2 && < 0.3
diff --git a/src/Data/Iteratee/IO.hs b/src/Data/Iteratee/IO.hs
--- a/src/Data/Iteratee/IO.hs
+++ b/src/Data/Iteratee/IO.hs
@@ -3,10 +3,14 @@
 -- |Random and Binary IO with generic Iteratees.
 
 module Data.Iteratee.IO(
+  -- * Data
+  defaultBufSize,
   -- * File enumerators
   -- ** Handle-based enumerators
   enumHandle,
   enumHandleRandom,
+  enumFile,
+  enumFileRandom,
 #if defined(USE_POSIX)
   -- ** FileDescriptor based enumerators
   enumFd,
@@ -35,6 +39,7 @@
 
 import Control.Monad.CatchIO
 
+-- | The default buffer size.
 defaultBufSize :: Int
 defaultBufSize = 1024
 
diff --git a/src/Data/Iteratee/IO/Fd.hs b/src/Data/Iteratee/IO/Fd.hs
--- a/src/Data/Iteratee/IO/Fd.hs
+++ b/src/Data/Iteratee/IO/Fd.hs
@@ -25,6 +25,7 @@
 import Data.Iteratee.Binary()
 import Data.Iteratee.IO.Base
 
+import Control.Concurrent (yield)
 import Control.Exception
 import Control.Monad
 import Control.Monad.CatchIO as CIO
@@ -37,7 +38,6 @@
 import System.IO (SeekMode(..))
 
 import System.Posix hiding (FileOffset)
-import GHC.Conc
 
 -- ------------------------------------------------------------------------
 -- Binary Random IO enumerators
@@ -50,11 +50,10 @@
   -> st
   -> m (Either SomeException ((Bool, st), s))
 makefdCallback p bufsize fd st = do
-  liftIO $ GHC.Conc.threadWaitRead fd
   n <- liftIO $ myfdRead fd (castPtr p) bufsize
   case n of
-    Left _   -> return $ Left undefined
-    Right 0  -> return $ Right ((False, st), empty)
+    Left  _  -> return $ Left (error "myfdRead failed")
+    Right 0  -> liftIO yield >> return (Right ((False, st), empty))
     Right n' -> liftM (\s -> Right ((True, st), s)) $
                   readFromPtr p (fromIntegral n')
 
diff --git a/src/Data/Iteratee/IO/Handle.hs b/src/Data/Iteratee/IO/Handle.hs
--- a/src/Data/Iteratee/IO/Handle.hs
+++ b/src/Data/Iteratee/IO/Handle.hs
@@ -10,6 +10,8 @@
   enumHandle
   ,enumHandleCatch
   ,enumHandleRandom
+  ,enumFile
+  ,enumFileRandom
   -- * Iteratee drivers
   ,fileDriverHandle
   ,fileDriverRandomHandle
@@ -105,33 +107,48 @@
 -- ----------------------------------------------
 -- File Driver wrapper functions.
 
-fileDriver :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
+enumFile' :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
   (Int -> Handle -> Enumerator s m a)
   -> Int -- ^Buffer size
-  -> Iteratee s m a
   -> FilePath
-  -> m a
-fileDriver enumf bufsize iter filepath = CIO.bracket
+  -> Enumerator s m a
+enumFile' enumf bufsize filepath iter = CIO.bracket
   (liftIO $ openBinaryFile filepath ReadMode)
   (liftIO . hClose)
-  (run <=< flip (enumf bufsize) iter)
+  (flip (enumf bufsize) iter)
 
+enumFile ::
+  (NullPoint s, MonadCatchIO m, ReadableChunk s el)
+  => Int                 -- ^Buffer size
+  -> FilePath
+  -> Enumerator s m a
+enumFile = enumFile' enumHandle
+
+enumFileRandom ::
+  (NullPoint s, MonadCatchIO m, ReadableChunk s el)
+  => Int                 -- ^Buffer size
+  -> FilePath
+  -> Enumerator s m a
+enumFileRandom = enumFile' enumHandleRandom
+
 -- |Process a file using the given @Iteratee@.  This function wraps
 -- @enumHandle@ as a convenience.
 fileDriverHandle
   :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
-     Int -- ^Buffer size (number of elements)
+     Int                      -- ^Buffer size (number of elements)
      -> Iteratee s m a
      -> FilePath
      -> m a
-fileDriverHandle = fileDriver enumHandle
+fileDriverHandle bufsize iter filepath =
+  enumFile bufsize filepath iter >>= run
 
 -- |A version of @fileDriverHandle@ that supports seeking.
 fileDriverRandomHandle
   :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
-     Int
+     Int                      -- ^ Buffer size (number of elements)
      -> Iteratee s m a
      -> FilePath
      -> m a
-fileDriverRandomHandle = fileDriver enumHandleRandom
+fileDriverRandomHandle bufsize iter filepath =
+  enumFileRandom bufsize filepath iter >>= run
 
diff --git a/src/Data/Iteratee/Iteratee.hs b/src/Data/Iteratee/Iteratee.hs
--- a/src/Data/Iteratee/Iteratee.hs
+++ b/src/Data/Iteratee/Iteratee.hs
@@ -1,4 +1,8 @@
-{-# LANGUAGE KindSignatures, FlexibleContexts, ScopedTypeVariables, DeriveDataTypeable #-}
+{-# LANGUAGE KindSignatures
+            ,RankNTypes
+            ,FlexibleContexts
+            ,ScopedTypeVariables
+            ,DeriveDataTypeable #-}
 
 -- |Monadic and General Iteratees:
 -- incremental input parsers, processors and transformers
@@ -34,6 +38,9 @@
   -- ** Enumerator Combinators
   ,(>>>)
   ,eneeCheckIfDone
+  -- ** Enumeratee Combinators
+  ,(><>)
+  ,(<><)
   -- * Misc.
   ,seek
   ,FileOffset
@@ -133,7 +140,6 @@
     step (Chunk xs)
       | nullC xs = liftI step
       | True     = lift (f xs) >> liftI step
-    step (Chunk xs) = lift (f xs) >> liftI step
     step s@(EOF _)  = idone () s
 {-# INLINE mapChunksM_ #-}
 
@@ -256,6 +262,32 @@
 
 (>>>) :: (Monad m) => Enumerator s m a -> Enumerator s m a -> Enumerator s m a
 (e1 >>> e2) i =  e1 i >>= e2
+  -- I think (>>>) is identical to (>=>)...
+
+
+-- | Enumeratee composition
+-- Run the second enumeratee within the first.  In this example, stream2list
+-- is run within the 'take 10', which is itself run within 'take 15', resulting
+-- in 15 elements being consumed
+--
+-- > run =<< enumPure1Chunk [1..1000 :: Int]
+-- >   (joinI $ (I.take 15 ><> I.take 10) I.stream2list)
+-- > [1,2,3,4,5,6,7,8,9,10]
+-- 
+(><>) ::
+ (Nullable s1, Monad m)
+  => (forall x . Enumeratee s1 s2 m x)
+  -> Enumeratee s2 s3 m a
+  -> Enumeratee s1 s3 m a
+f ><> g = joinI . f . g
+
+-- | enumeratee composition with the arguments flipped, see '><>'
+(<><) ::
+ (Nullable s1, Monad m)
+  => Enumeratee s2 s3 m a
+  -> (forall x. Enumeratee s1 s2 m x)
+  -> Enumeratee s1 s3 m a
+f <>< g = joinI . g . f
 
 -- |The pure 1-chunk enumerator
 -- It passes a given list of elements to the iteratee in one chunk
diff --git a/src/Data/Iteratee/ListLike.hs b/src/Data/Iteratee/ListLike.hs
--- a/src/Data/Iteratee/ListLike.hs
+++ b/src/Data/Iteratee/ListLike.hs
@@ -45,6 +45,12 @@
   ,enumPureNChunk
   -- ** Enumerator Combinators
   ,enumPair
+  ,enumWith
+  ,zip
+  ,zip3
+  ,zip4
+  ,zip5
+  ,sequence_
   -- ** Monadic functions
   ,mapM_
   ,foldM
@@ -53,19 +59,22 @@
 )
 where
 
-import Prelude hiding (mapM_, null, head, last, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product)
+import Prelude hiding (mapM_, null, head, last, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product, zip, zip3, sequence_)
 
+import qualified Prelude as Prelude
+
 import qualified Data.ListLike as LL
 import qualified Data.ListLike.FoldableLL as FLL
 import Data.Iteratee.Iteratee
 import Data.Monoid
+import Data.Maybe (catMaybes)
 import Control.Applicative
+import Control.Monad (liftM, liftM2, mplus, (<=<))
 import Control.Monad.Trans.Class
 import Data.Word (Word8)
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as BC
 
-
 -- Useful combinators for implementing iteratees and enumerators
 
 -- | Check if a stream has received 'EOF'.
@@ -199,8 +208,9 @@
 
 -- | Return a chunk of `t' elements length, while consuming `d' elements
 --   from the stream.  Useful for creating a "rolling average" with convStream.
-roll :: (Monad m, Functor m, Nullable s, LL.ListLike s el, LL.ListLike s' s) =>
-  Int
+roll
+  :: (Monad m, Functor m, Nullable s, LL.ListLike s el, LL.ListLike s' s)
+  => Int
   -> Int
   -> Iteratee s m s'
 roll t d | t > d  = liftI step
@@ -271,7 +281,10 @@
 -- @break@ pred === @joinI@ (@breakE@ pred stream2stream)
 --
 -- @breakE@ should be used in preference to @break@ whenever possible.
-breakE :: (Monad m, LL.ListLike s el, NullPoint s) => (el -> Bool) -> Enumeratee s s m a
+breakE
+  :: (Monad m, LL.ListLike s el, NullPoint s)
+  => (el -> Bool)
+  -> Enumeratee s s m a
 breakE cpred = eneeCheckIfDone (liftI . step)
  where
   step k (Chunk s)
@@ -342,14 +355,14 @@
 -- given iteratee to it.
 --
 -- The analog of @List.map@
-mapStream ::
- (Monad m,
-  LL.ListLike (s el) el,
-  LL.ListLike (s el') el',
-  NullPoint (s el),
-  LooseMap s el el') =>
- (el -> el')
- -> Enumeratee (s el) (s el') m a
+mapStream
+  :: (Monad m
+     ,LL.ListLike (s el) el
+     ,LL.ListLike (s el') el'
+     ,NullPoint (s el)
+     ,LooseMap s el el')
+  => (el -> el')
+  -> Enumeratee (s el) (s el') m a
 mapStream f = eneeCheckIfDone (liftI . step)
   where
     step k (Chunk xs)
@@ -363,9 +376,9 @@
 -- Like 'mapStream', but the element type cannot change.
 -- This function is necessary for @ByteString@ and similar types
 -- that cannot have 'LooseMap' instances, and may be more efficient.
-rigidMapStream ::
- (Monad m, LL.ListLike s el, NullPoint s) =>
-  (el -> el)
+rigidMapStream
+  :: (Monad m, LL.ListLike s el, NullPoint s)
+  => (el -> el)
   -> Enumeratee s s m a
 rigidMapStream f = eneeCheckIfDone (liftI . step)
   where
@@ -381,9 +394,9 @@
 -- satisfy the predicate function.  The outer stream is completely consumed.
 --
 -- The analogue of @List.filter@
-filter ::
- (Monad m, Nullable s, LL.ListLike s el) =>
-  (el -> Bool)
+filter
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => (el -> Bool)
   -> Enumeratee s s m a
 filter p = convStream f'
   where
@@ -397,8 +410,10 @@
 -- |Creates an 'enumeratee' in which elements from the stream are
 -- grouped into \sz\-sized blocks.  The outer stream is completely
 -- consumed and the final block may be smaller than \sz\.
-group :: (LL.ListLike s el, Monad m, Nullable s) => 
-             Int -> Enumeratee s [s] m a
+group
+  :: (LL.ListLike s el, Monad m, Nullable s)
+  => Int
+  -> Enumeratee s [s] m a
 group sz iinit = liftI $ go iinit LL.empty
   where go icurr pfx (Chunk s) = case gsplit (pfx `LL.append` s) of 
           (full, partial) | LL.null full -> liftI $ go icurr partial
@@ -422,8 +437,10 @@
 -- 
 -- The analogue of @List.groupBy#
           
-groupBy :: (LL.ListLike s el, Monad m, Nullable s) =>
-               (el -> el -> Bool) -> Enumeratee s [s] m a
+groupBy
+  :: (LL.ListLike s el, Monad m, Nullable s)
+  => (el -> el -> Bool)
+  -> Enumeratee s [s] m a
 groupBy same iinit = liftI $ go iinit LL.empty
     where go icurr pfx (Chunk s) = case gsplit (pfx `LL.append` s) of
                                           (full, partial)
@@ -453,9 +470,9 @@
 -- | Left-associative fold.
 --
 -- The analogue of @List.foldl@
-foldl ::
- (Monad m, LL.ListLike s el, FLL.FoldableLL s el) =>
-  (a -> el -> a)
+foldl
+  :: (Monad m, LL.ListLike s el, FLL.FoldableLL s el)
+  => (a -> el -> a)
   -> a
   -> Iteratee s m a
 foldl f i = liftI (step i)
@@ -471,9 +488,9 @@
 -- This function should be used in preference to 'foldl' whenever possible.
 --
 -- The analogue of @List.foldl'@.
-foldl' ::
- (Monad m, LL.ListLike s el, FLL.FoldableLL s el) =>
-  (a -> el -> a)
+foldl'
+  :: (Monad m, LL.ListLike s el, FLL.FoldableLL s el)
+  => (a -> el -> a)
   -> a
   -> Iteratee s m a
 foldl' f i = liftI (step i)
@@ -488,9 +505,9 @@
 --   in the stream.
 --
 -- The analogue of @List.foldl1@.
-foldl1 ::
- (Monad m, LL.ListLike s el, FLL.FoldableLL s el) =>
-  (el -> el -> el)
+foldl1
+  :: (Monad m, LL.ListLike s el, FLL.FoldableLL s el)
+  => (el -> el -> el)
   -> Iteratee s m el
 foldl1 f = liftI step
   where
@@ -503,9 +520,9 @@
 
 
 -- | Strict variant of 'foldl1'.
-foldl1' ::
- (Monad m, LL.ListLike s el, FLL.FoldableLL s el) =>
-  (el -> el -> el)
+foldl1'
+  :: (Monad m, LL.ListLike s el, FLL.FoldableLL s el)
+  => (el -> el -> el)
   -> Iteratee s m el
 foldl1' f = liftI step
   where
@@ -543,42 +560,176 @@
 -- Zips
 
 -- |Enumerate two iteratees over a single stream simultaneously.
+--  Deprecated, use `Data.Iteratee.ListLike.zip` instead.
 --
 -- Compare to @zip@.
-enumPair ::
- (Monad m, Nullable s, LL.ListLike s el) =>
-  Iteratee s m a
+{-# DEPRECATED enumPair "use Data.Iteratee.ListLike.zip" #-}
+enumPair
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a
   -> Iteratee s m b
-  -> Iteratee s m (a,b)
-enumPair i1 i2 = Iteratee $ \od oc -> runIter i1 (onDone od oc) (onCont od oc)
+  -> Iteratee s m (a, b)
+enumPair = zip
+
+
+-- |Enumerate two iteratees over a single stream simultaneously.
+--
+-- Compare to @zip@.
+zip
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a
+  -> Iteratee s m b
+  -> Iteratee s m (a, b)
+zip x y = liftI step
   where
-    onDone od oc x s        = runIter i2 (oD12 od oc x s) (onCont' od oc x)
-    oD12 od oc x1 s1 x2 s2  = runIter (idone (x1,x2) (longest s1 s2)) od oc
-    onCont od oc k mErr     = runIter (icont (step k) mErr) od oc
+    step (Chunk xs) | nullC xs = liftI step
+    step (Chunk xs) = do
+      (a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x
+      (b', y') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs y
+      case checkDone a' b' of
+        Just (a, b, s) -> idone (a, b) s
+        Nothing        -> zip x' y'
+    step (EOF err) = joinIM $ case err of
+      Nothing -> (liftM2.liftM2) (,) (enumEof   x) (enumEof   y)
+      Just e  -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)
+
+    od a s = return (Just (a, s), idone a s)
+    oc k e = return (Nothing    , icont k e)
+
+    checkDone r1 r2 =
+      r1 >>= \(a, s1) -> r2 >>= \(b, s2) ->
+      return (a, b, shorter s1 s2)
+
+    shorter c1@(Chunk xs) c2@(Chunk ys)
+      | LL.length xs < LL.length ys = c1
+      | otherwise                   = c2
+    shorter e@(EOF _)  _         = e
+    shorter _          e@(EOF _) = e
+{-# INLINE zip #-}
+
+zip3
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a -> Iteratee s m b
+  -> Iteratee s m c -> Iteratee s m (a, b, c)
+zip3 a b c = zip a (zip b c) >>=
+  \(r1, (r2, r3)) -> return (r1, r2, r3)
+{-# INLINE zip3 #-}
+
+zip4
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a -> Iteratee s m b
+  -> Iteratee s m c -> Iteratee s m d
+  -> Iteratee s m (a, b, c, d)
+zip4 a b c d = zip a (zip3 b c d) >>=
+  \(r1, (r2, r3, r4)) -> return (r1, r2, r3, r4)
+{-# INLINE zip4 #-}
+
+zip5
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a -> Iteratee s m b
+  -> Iteratee s m c -> Iteratee s m d
+  -> Iteratee s m e -> Iteratee s m (a, b, c, d, e)
+zip5 a b c d e = zip a (zip4 b c d e) >>=
+  \(r1, (r2, r3, r4, r5)) -> return (r1, r2, r3, r4, r5)
+{-# INLINE zip5 #-}
+
+-- | Enumerate over two iteratees in parallel as long as the first iteratee
+-- is still consuming input.  The second iteratee will be terminated with EOF
+-- when the first iteratee has completed.  An example use is to determine
+-- how many elements an iteratee has consumed:
+--
+-- > snd <$> enumWith (dropWhile (<5)) length
+--
+-- Compare to @zip@
+enumWith
+  :: (Monad m, Nullable s, LL.ListLike s el)
+  => Iteratee s m a
+  -> Iteratee s m b
+  -> Iteratee s m (a, b)
+enumWith i1 i2 = go i1 i2
+  where
+    od a s = return (Just (a, s), idone a s)
+    oc k e = return (Nothing    , icont k e)
+
+    getUsed xs (Chunk ys) = LL.take (LL.length xs - LL.length ys) xs
+    getUsed xs (EOF _)    = xs
+
+    go x y = liftI step
       where
-    onCont' od oc x1 k mErr = runIter (icont (step2 x1 k) mErr) od oc
-    step k c@(Chunk str)
-      | nullC str            = liftI (step k)
-      | True                = lift (enumPure1Chunk str i2) >>= enumPair (k c)
-    step k s@(EOF Nothing)  = lift (enumEof i2) >>= enumPair (k s)
-    step k s@(EOF (Just e)) = lift (enumErr e i2) >>= enumPair (k s)
-    step2 x1 k (Chunk str)
-      | nullC str            = liftI (step2 x1 k)
-    step2 x1 k str          = enumPair (return x1) (k str)
-    longest c1@(Chunk xs) c2@(Chunk ys) = if LL.length xs > LL.length ys
-                                          then c1 else c2
-    longest e@(EOF _)  _         = e
-    longest _          e@(EOF _) = e
-{-# INLINE enumPair #-}
+        step (Chunk xs) | nullC xs = liftI step
+        step (Chunk xs) = do
+          (a', x') <- lift $ (\i -> runIter i od oc) =<< enumPure1Chunk xs x
+          case a' of
+            Just (a, s) -> do
+              b <- lift $ run =<< enumPure1Chunk (getUsed xs s) y
+              idone (a, b) s
+            Nothing        -> lift (enumPure1Chunk xs y) >>= go x'
+        step (EOF err) = joinIM $ case err of
+          Nothing -> (liftM2.liftM2) (,) (enumEof   x) (enumEof   y)
+          Just e  -> (liftM2.liftM2) (,) (enumErr e x) (enumErr e y)
+{-# INLINE enumWith #-}
 
+-- |Enumerate a list of iteratees over a single stream simultaneously
+-- and discard the results. This is a different behavior than Prelude's
+-- sequence_ which runs iteratees in the list one after the other.
+--
+-- Compare to @sequence_@.
+sequence_
+  :: (Monad m, LL.ListLike s el, Nullable s)
+  => [Iteratee s m a]
+  -> Iteratee s m ()
+sequence_ = self
+  where
+    self is = liftI step
+      where
+        step (Chunk xs) | LL.null xs = liftI step
+        step s@(Chunk _) = do
+          -- give a chunk to each iteratee
+          is'  <- lift $ mapM (enumChunk s) is
+          -- filter done iteratees
+          is'' <- lift $ catMaybes `liftM` mapM checkIfDone is'
+          if Prelude.null is''
+            then idone () <=< remainingStream $ is'
+            else self is''
+        step s@(EOF _) = do
+          s' <- remainingStream <=< lift $ mapM (enumChunk s) $ is
+          case s' of
+            EOF (Just e) -> throwErr e
+            _            -> idone () s'
 
+        checkIfDone i = runIter i
+            (\_ _ -> return Nothing)
+            (\k e -> return $ Just $ icont k e)
+
+    -- returns the unconsumed part of the stream; "sequence_ is" consumes as
+    -- much of the stream as the iteratee in is that consumes the most; e.g.
+    -- sequence_ [I.head, I.last] consumes whole stream
+    remainingStream
+      :: (Monad m, Nullable s, LL.ListLike s el)
+      => [Iteratee s m a] -> Iteratee s m (Stream s)
+    remainingStream is = lift $
+      return . Prelude.foldl1 shorter <=< mapM (\i -> runIter i od oc) $ is
+      where
+        od _ s = return s
+        oc _ e = return $ case e of
+          Nothing -> mempty
+          _       -> EOF e
+
+    -- return the shorter one of two streams; errors are propagated with the
+    -- priority given to the "left"
+    shorter c1@(Chunk xs) c2@(Chunk ys)
+      | LL.length xs < LL.length ys = c1
+      | otherwise                   = c2
+    shorter (EOF e1 ) (EOF e2 ) = EOF (e1 `mplus` e2)
+    shorter e@(EOF _) _         = e
+    shorter _         e@(EOF _) = e
+
 -- ------------------------------------------------------------------------
 -- Enumerators
 
 -- |The pure n-chunk enumerator
 -- It passes a given stream of elements to the iteratee in @n@-sized chunks.
-enumPureNChunk ::
- (Monad m, LL.ListLike s el) => s -> Int -> Enumerator s m a
+enumPureNChunk :: (Monad m, LL.ListLike s el) => s -> Int -> Enumerator s m a
 enumPureNChunk str n iter
   | LL.null str = return iter
   | n > 0       = enum' str iter
@@ -598,9 +749,10 @@
 
 -- | Map a monadic function over the elements of the stream and ignore the
 -- result.
-mapM_ :: (Monad m, LL.ListLike s el, Nullable s)
-      => (el -> m b)
-      -> Iteratee s m ()
+mapM_
+  :: (Monad m, LL.ListLike s el, Nullable s)
+  => (el -> m b)
+  -> Iteratee s m ()
 mapM_ f = liftI step
   where
     step (Chunk xs) | LL.null xs = liftI step
@@ -609,10 +761,11 @@
 {-# INLINE mapM_ #-}
 
 -- |The analogue of @Control.Monad.foldM@
-foldM :: (Monad m, LL.ListLike s b, Nullable s)
-      => (a -> b -> m a)
-      -> a
-      -> Iteratee s m a
+foldM
+  :: (Monad m, LL.ListLike s b, Nullable s)
+  => (a -> b -> m a)
+  -> a
+  -> Iteratee s m a
 foldM f e = liftI step
   where
     step (Chunk xs) | LL.null xs = liftI step
diff --git a/tests/foo.hs b/tests/foo.hs
deleted file mode 100644
--- a/tests/foo.hs
+++ /dev/null
@@ -1,11 +0,0 @@
-import Control.Monad.Trans (liftIO)
-import Data.Iteratee.Iteratee
-import qualified Data.Iteratee.ListLike as I
-
-main = enumPure1Chunk [1..10] myIter >>= run
-
-myIter :: Iteratee [Int] IO ()
-myIter = do
-   I.last
-   x <- I.peek
-   liftIO $ print x
diff --git a/tests/testIteratee.hs b/tests/testIteratee.hs
--- a/tests/testIteratee.hs
+++ b/tests/testIteratee.hs
@@ -10,14 +10,17 @@
 
 import Test.QuickCheck
 
-import Data.Iteratee hiding (head, break)
+import           Data.Iteratee hiding (head, break)
 import qualified Data.Iteratee.Char as IC
 import qualified Data.Iteratee as Iter
-import Data.Functor.Identity
+import           Data.Functor.Identity
 import qualified Data.List as List (groupBy, unfoldr)
-import Data.Monoid
+import           Data.Monoid
 import qualified Data.ListLike as LL
 
+import           Control.Monad as CM
+import           Control.Monad.Writer
+
 import Text.Printf (printf)
 import System.Environment (getArgs)
 
@@ -53,8 +56,8 @@
 -- ---------------------------------------------
 -- Iteratee instances
 
-runner0 = runIdentity . Iter.run
 runner1 = runIdentity . Iter.run . runIdentity
+enumSpecial xs n = enumPure1Chunk LL.empty >=> enumPureNChunk xs n
 
 prop_iterFmap xs f a = runner1 (enumPure1Chunk xs (fmap f $ return a))
                      == runner1 (enumPure1Chunk xs (return $ f a))
@@ -64,15 +67,15 @@
                       == f (runner1 (enumPure1Chunk xs i))
   where types = (xs :: [Int], i :: I, f :: [Int] -> [Int])
 
-prop_iterMonad1 xs a f = runner1 (enumPureNChunk xs 1 (return a >>= f))
+prop_iterMonad1 xs a f = runner1 (enumSpecial xs 1 (return a >>= f))
                        == runner1 (enumPure1Chunk xs (f a))
   where types = (xs :: [Int], a :: Int, f :: Int -> I)
 
-prop_iterMonad2 m xs = runner1 (enumPureNChunk xs 1 (m >>= return))
+prop_iterMonad2 m xs = runner1 (enumSpecial xs 1 (m >>= return))
                      == runner1 (enumPure1Chunk xs m)
   where types = (xs :: [Int], m :: I)
 
-prop_iterMonad3 m f g xs = runner1 (enumPureNChunk xs 1 ((m >>= f) >>= g))
+prop_iterMonad3 m f g xs = runner1 (enumSpecial xs 1 ((m >>= f) >>= g))
                          == runner1 (enumPure1Chunk xs (m >>= (\x -> f x >>= g)))
   where types = (xs :: [Int], m :: I, f :: [Int] -> I, g :: [Int] -> I)
 
@@ -82,7 +85,7 @@
 prop_list xs = runner1 (enumPure1Chunk xs stream2list) == xs
   where types = xs :: [Int]
 
-prop_clist xs n = n > 0 ==> runner1 (enumPureNChunk xs n stream2list) == xs
+prop_clist xs n = n > 0 ==> runner1 (enumSpecial xs n stream2list) == xs
   where types = xs :: [Int]
 
 prop_break f xs = runner1 (enumPure1Chunk xs (Iter.break f)) == fst (break f xs)
@@ -104,7 +107,8 @@
 prop_head2 xs = P.length xs > 0 ==> runner1 (enumPure1Chunk xs (Iter.head >> stream2list)) == tail xs
   where types = xs :: [Int]
 
-prop_heads xs = runner1 (enumPure1Chunk xs $ heads xs) == P.length xs
+prop_heads xs n = n > 0 ==>
+ runner1 (enumSpecial xs n $ heads xs) == P.length xs
   where types = xs :: [Int]
 
 prop_heads2 xs = runner1 (enumPure1Chunk xs $ heads [] >>= \c ->
@@ -132,13 +136,25 @@
  runner1 (enumPure1Chunk xs (Iter.last >> Iter.peek)) == Nothing
   where types = xs :: [Int]
 
+prop_drop xs n k = (n > 0 && k >= 0) ==>
+ runner1 (enumSpecial xs n (Iter.drop k >> stream2list)) == P.drop k xs
+  where types = xs :: [Int]
+
+prop_dropWhile f xs =
+ runner1 (enumPure1Chunk xs (Iter.dropWhile f >> stream2list))
+ == P.dropWhile f xs
+  where types = (xs :: [Int], f :: Int -> Bool)
+
+prop_length xs = runner1 (enumPure1Chunk xs Iter.length) == P.length xs
+  where types = xs :: [Int]
+
 -- ---------------------------------------------
 -- Simple enumerator tests
 
 type I = Iteratee [Int] Identity [Int]
 
 prop_enumChunks n xs i = n > 0  ==>
-  runner1 (enumPure1Chunk xs i) == runner1 (enumPureNChunk xs n i)
+  runner1 (enumPure1Chunk xs i) == runner1 (enumSpecial xs n i)
   where types = (n :: Int, xs :: [Int], i :: I)
 
 prop_app1 xs ys i = runner1 (enumPure1Chunk ys (joinIM $ enumPure1Chunk xs i))
@@ -170,7 +186,20 @@
                 runner1 (enumPure1Chunk xs =<< enumPure1Chunk [] Iter.head)
                 == runner1 (enumPure1Chunk xs Iter.head)
   where types = xs :: [Int]
+
 -- ---------------------------------------------
+-- Enumerator Combinators
+
+prop_enumWith xs f n = n > 0 ==> runner1 (enumSpecial xs n $ fmap fst $ enumWith (Iter.dropWhile f) (stream2list))
+   == runner1 (enumSpecial xs n $ Iter.dropWhile f)
+ where types = (xs :: [Int])
+
+prop_enumWith2 xs f n = n > 0 ==> runner1 (enumSpecial xs n $ enumWith (Iter.dropWhile f) (stream2list) >> stream2list)
+   == runner1 (enumSpecial xs n $ Iter.dropWhile f >> stream2list)
+ where types = (xs :: [Int])
+
+
+-- ---------------------------------------------
 -- Nested Iteratees
 
 -- take, mapStream, convStream, and takeR
@@ -182,7 +211,7 @@
   where types = (i :: I, xs :: [Int])
 
 prop_mapStream2 xs n i = n > 0 ==>
-                         runner2 (enumPureNChunk xs n $ mapStream id i)
+                         runner2 (enumSpecial xs n $ mapStream id i)
                          == runner1 (enumPure1Chunk xs i)
   where types = (i :: I, xs :: [Int])
 
@@ -191,7 +220,33 @@
   == runner1 (enumPure1Chunk xs i)
   where types = (i :: I, xs :: [Int])
 
+prop_rigidMapStream xs n f = n > 0 ==>
+ runner2 (enumSpecial xs n $ rigidMapStream f stream2list) == map f xs
+  where types = (xs :: [Int])
 
+prop_foldl xs n f x0 = n > 0 ==>
+ runner1 (enumSpecial xs n (Iter.foldl f x0)) == P.foldl f x0 xs
+  where types = (xs :: [Int], x0 :: Int)
+
+prop_foldl' xs n f x0 = n > 0 ==>
+ runner1 (enumSpecial xs n (Iter.foldl' f x0)) == LL.foldl' f x0 xs
+  where types = (xs :: [Int], x0 :: Int)
+
+prop_foldl1 xs n f = (n > 0 && not (null xs)) ==>
+ runner1 (enumSpecial xs n (Iter.foldl1 f)) == P.foldl1 f xs
+  where types = (xs :: [Int])
+
+prop_foldl1' xs n f = (n > 0 && not (null xs)) ==>
+ runner1 (enumSpecial xs n (Iter.foldl1' f)) == P.foldl1 f xs
+  where types = (xs :: [Int])
+
+prop_sum xs n = n > 0 ==> runner1 (enumSpecial xs n Iter.sum) == P.sum xs
+  where types = (xs :: [Int])
+
+prop_product xs n = n > 0 ==>
+ runner1 (enumSpecial xs n Iter.product) == P.product xs
+  where types = (xs :: [Int])
+
 convId :: (LL.ListLike s el, Monad m) => Iteratee s m s
 convId = liftI (\str -> case str of
   s@(Chunk xs) | LL.null xs -> convId
@@ -232,6 +287,14 @@
                   == runner2 (enumPure1Chunk xs $ takeUpTo n stream2list)
   where types = xs :: [Int]
 
+prop_takeUpTo2 xs n = n >= 0 ==>
+ runner2 (enumPure1Chunk xs (takeUpTo n identity)) == ()
+  where types = xs :: [Int]
+
+prop_filter xs n f = n > 0 ==>
+ runner2 (enumSpecial xs n (Iter.filter f stream2list)) == P.filter f xs
+  where types = xs :: [Int]
+
 prop_group xs n = n > 0 ==>
                   runner2 (enumPure1Chunk xs $ Iter.group n stream2list)
                   == runner1 (enumPure1Chunk groups stream2list)
@@ -247,6 +310,18 @@
   where types = xs :: [Int]
         pred z1 z2 = (z1 `mod` m == z2 `mod` m)
 
+prop_mapM_ xs n = n > 0 ==>
+ runWriter ((enumSpecial xs n (Iter.mapM_ f)) >>= run)
+ == runWriter (CM.mapM_ f xs)
+  where f = const $ tell (Sum 1)
+        types = xs :: [Int]
+
+prop_foldM xs x0 n = n > 0 ==>
+ runWriter ((enumSpecial xs n (Iter.foldM f x0)) >>= run)
+ == runWriter (CM.foldM f x0 xs)
+  where f acc el = tell (Sum 1) >> return (acc - el)
+        types = xs :: [Int]
+
 -- ---------------------------------------------
 -- Data.Iteratee.Char
 
@@ -284,6 +359,9 @@
     ,testProperty "peek2" prop_peek2
     ,testProperty "last" prop_last1
     ,testProperty "last ends properly" prop_last2
+    ,testProperty "length" prop_length
+    ,testProperty "drop" prop_drop
+    ,testProperty "dropWhile" prop_dropWhile
     ,testProperty "skipToEof" prop_skip
     ,testProperty "iteratee Functor 1" prop_iterFmap
     ,testProperty "iteratee Functor 2" prop_iterFmap2
@@ -306,20 +384,39 @@
     testProperty "mapStream identity" prop_mapStream
     ,testProperty "mapStream identity 2" prop_mapStream2
     ,testProperty "mapStream identity joinI" prop_mapjoin
+    ,testProperty "rigidMapStream" prop_rigidMapStream
     ,testProperty "breakE" prop_breakE
     ,testProperty "breakE remainder" prop_breakE2
     ,testProperty "take" prop_take
     ,testProperty "take (finished iteratee)" prop_take2
     ,testProperty "takeUpTo" prop_takeUpTo
+    ,testProperty "takeUpTo (finished iteratee)" prop_takeUpTo2
+    ,testProperty "filter" prop_filter
     ,testProperty "group" prop_group
     ,testProperty "groupBy" prop_groupBy
     ,testProperty "convStream EOF" prop_convstream2
     ,testProperty "convStream identity" prop_convstream
     ,testProperty "convStream identity 2" prop_convstream3
     ]
+  ,testGroup "Enumerator Combinators" [
+    testProperty "enumWith" prop_enumWith
+    ,testProperty "enumWith remaining" prop_enumWith2
+    ]
+  ,testGroup "Folds" [
+    testProperty "foldl" prop_foldl
+   ,testProperty "foldl'" prop_foldl'
+   ,testProperty "foldl1" prop_foldl1
+   ,testProperty "foldl1'" prop_foldl1'
+   ,testProperty "sum" prop_sum
+   ,testProperty "product" prop_product
+   ]
   ,testGroup "Data.Iteratee.Char" [
     --testProperty "line" prop_line
     ]
+  ,testGroup "Monadic functions" [
+    testProperty "mapM_" prop_mapM_
+   ,testProperty "foldM" prop_foldM
+   ]
   ]
 
 ------------------------------------------------------------------------
