diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.8.6.2
+version:       0.8.7
 synopsis:      Iteratee-based I/O
 description:
   The Iteratee monad provides strict, safe, and functional I/O. In addition
@@ -12,7 +12,7 @@
 license:       BSD3
 license-file:  LICENSE
 homepage:      http://www.tiresiaspress.us/haskell/iteratee
-tested-with:   GHC == 7.0.3, GHC == 6.12.3
+tested-with:   GHC == 7.0.4, GHC == 7.2.1
 stability:     experimental
 
 cabal-version: >= 1.6
@@ -26,9 +26,6 @@
   Examples/*.txt
   tests/*.hs
 
-flag splitBase
-  description: Use the split-up base package.
-
 flag buildTests
   description: Build test executables.
   default:     False
@@ -37,13 +34,6 @@
   hs-source-dirs:
     src
 
-  if flag(splitBase)
-    build-depends:
-      base >= 3 && < 6
-  else
-    build-depends:
-      base < 3
-
   if os(windows)
     cpp-options: -DUSE_WINDOWS
     exposed-modules:
@@ -57,6 +47,7 @@
       unix >= 2 && < 3
 
   build-depends:
+    base                      >= 3       && < 6,
     ListLike                  >= 1.0     && < 4,
     MonadCatchIO-transformers >  0.2     && < 0.3,
     bytestring                >= 0.9     && < 0.10,
@@ -100,19 +91,13 @@
 
   if flag(buildTests)
     build-depends:
+      base                       >= 3   && < 6,
       mtl                        >= 2   && < 3,
       QuickCheck                 >= 2   && < 3,
       test-framework             >= 0.3 && < 0.4,
       test-framework-quickcheck2 >= 0.2 && < 0.3
   else
     buildable:  False
-
-  if flag(splitBase)
-    build-depends:
-      base >= 3 && < 5
-  else
-    build-depends:
-      base < 3
 
 source-repository head
   type:     darcs
diff --git a/src/Data/Iteratee/Base.hs b/src/Data/Iteratee/Base.hs
--- a/src/Data/Iteratee/Base.hs
+++ b/src/Data/Iteratee/Base.hs
@@ -27,17 +27,15 @@
   -- ** Stream Functions
   ,setEOF
   -- * Classes
-  ,module Data.NullPoint
-  ,module Data.Nullable
-  ,module Data.Iteratee.Base.LooseMap
+  ,module X
 )
 where
 
 import Prelude hiding (null, catch)
-import Data.Iteratee.Base.LooseMap
 import Data.Iteratee.Exception
-import Data.Nullable
-import Data.NullPoint
+import Data.Iteratee.Base.LooseMap as X
+import Data.Nullable               as X
+import Data.NullPoint              as X
 import Data.Monoid
 
 import Control.Monad.IO.Class
@@ -186,6 +184,7 @@
 -- Note that only internal iteratee exceptions will be returned; exceptions
 -- thrown with @Control.Exception.throw@ or @Control.Monad.CatchIO.throw@ will
 -- not be returned.
+-- 
 -- See 'Data.Iteratee.Exception.IFException' for details.
 tryRun :: (Exception e, Monad m) => Iteratee s m a -> m (Either e a)
 tryRun iter = runIter iter onDone onCont
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
@@ -76,8 +76,8 @@
  forall e s el m a.(IException e,
                     NullPoint s,
                     ReadableChunk s el,
-                    MonadCatchIO m) =>
-  Int -- ^Buffer size (number of elements per read)
+                    MonadCatchIO m)
+  => Int -- ^Buffer size (number of elements per read)
   -> Handle
   -> (e -> m (Maybe EnumException))
   -> Enumerator s m a
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
@@ -10,8 +10,9 @@
 
 module Data.Iteratee.Iteratee (
   -- * Types
+  EnumerateeHandler
   -- ** Error handling
-  throwErr
+  ,throwErr
   ,throwRecoverableErr
   ,checkErr
   -- ** Basic Iteratees
@@ -20,12 +21,15 @@
   ,isStreamFinished
   -- ** Chunkwise Iteratees
   ,mapChunksM_
+  ,foldChunksM
   ,getChunk
   ,getChunks
   -- ** Nested iteratee combinators
   ,mapChunks
+  ,mapChunksM
   ,convStream
   ,unfoldConvStream
+  ,unfoldConvStreamCheck
   ,joinI
   ,joinIM
   -- * Enumerators
@@ -43,6 +47,9 @@
   -- ** Enumerator Combinators
   ,(>>>)
   ,eneeCheckIfDone
+  ,eneeCheckIfDoneHandle
+  ,eneeCheckIfDoneIgnore
+  ,eneeCheckIfDonePass
   ,mergeEnums
   -- ** Enumeratee Combinators
   ,(><>)
@@ -150,6 +157,14 @@
     step s@(EOF _) = idone () s
 {-# INLINE mapChunksM_ #-}
 
+-- | A fold over chunks
+foldChunksM :: (Monad m, Nullable s) => (a -> s -> m a) -> a -> Iteratee s m a
+foldChunksM f = liftI . go
+  where
+    go a (Chunk c) = lift (f a c) >>= liftI . go
+    go a e = idone a e
+{-# INLINE foldChunksM #-}
+
 -- | Get the current chunk from the stream.
 getChunk :: (Monad m, Nullable s, NullPoint s) => Iteratee s m s
 getChunk = liftI step
@@ -209,6 +224,47 @@
       onCont _ (Just e) = runIter (throwErr e) od oc
   in runIter inner onDone onCont
 
+
+type EnumerateeHandler eli elo m a =
+  (Stream eli -> Iteratee eli m a)
+  -> SomeException
+  -> Iteratee elo m (Iteratee eli m a)
+
+-- | The same as eneeCheckIfDonePass, with one extra argument:
+-- a handler which is used
+-- to process any exceptions in a separate method.
+eneeCheckIfDoneHandle
+  :: (Monad m, NullPoint elo)
+  => EnumerateeHandler eli elo m a
+  -> ((Stream eli -> Iteratee eli m a)
+      -> Maybe SomeException
+      -> Iteratee elo m (Iteratee eli m a)
+     )
+  -> Enumeratee elo eli m a
+eneeCheckIfDoneHandle h f inner = Iteratee $ \od oc ->
+  let onDone x s = od (idone x s) (Chunk empty)
+      onCont k Nothing  = runIter (f k Nothing) od oc
+      onCont k (Just e) = runIter (h k e)       od oc
+  in runIter inner onDone onCont
+
+eneeCheckIfDonePass
+  :: (Monad m, NullPoint elo)
+  => ((Stream eli -> Iteratee eli m a)
+      -> Maybe SomeException
+      -> Iteratee elo m (Iteratee eli m a)
+     )
+  -> Enumeratee elo eli m a
+eneeCheckIfDonePass f = eneeCheckIfDoneHandle (\k e -> f k (Just e)) f
+
+eneeCheckIfDoneIgnore
+  :: (Monad m, NullPoint elo)
+  => ((Stream eli -> Iteratee eli m a)
+      -> Maybe SomeException
+      -> Iteratee elo m (Iteratee eli m a)
+     )
+  -> Enumeratee elo eli m a
+eneeCheckIfDoneIgnore f = eneeCheckIfDoneHandle (\k _ -> f k Nothing) f
+
 -- | Convert one stream into another with the supplied mapping function.
 -- This function operates on whole chunks at a time, contrasting to
 -- @mapStream@ which operates on single elements.
@@ -223,6 +279,17 @@
   step k str@(EOF mErr) = idone (k $ EOF mErr) str
 {-# INLINE mapChunks #-}
 
+-- | Convert a stream of @s@ to a stream of @s'@ using the supplied function.
+mapChunksM
+  :: (Monad m, NullPoint s, Nullable s)
+  => (s -> m s')
+  -> Enumeratee s s' m a
+mapChunksM f = eneeCheckIfDone (liftI . step)
+ where
+  step k (Chunk xs)     = lift (f xs) >>=
+                          eneeCheckIfDone (liftI . step) . k . Chunk
+  step k str@(EOF mErr) = idone (k $ EOF mErr) str
+{-# INLINE mapChunksM #-}
 
 -- |Convert one stream into another, not necessarily in lockstep.
 -- The transformer mapStream maps one element of the outer stream
@@ -255,6 +322,27 @@
     step acc k = f acc >>= \(acc', s') ->
                     eneeCheckIfDone (check acc') . k . Chunk $ s'
 
+unfoldConvStreamCheck
+  :: (Monad m, Nullable elo, Monoid elo)
+  => (((Stream eli -> Iteratee eli m a)
+        -> Maybe SomeException
+        -> Iteratee elo m (Iteratee eli m a)
+      )
+      -> Enumeratee elo eli m a
+     )
+  -> (acc -> Iteratee elo m (acc, eli))
+  -> acc
+  -> Enumeratee elo eli m a
+unfoldConvStreamCheck checkDone f acc0 = checkDone (check acc0)
+  where
+    check acc k mX = isStreamFinished >>=
+                   maybe (step acc k mX) (idone (icont k mX) . EOF . Just)
+    step acc k Nothing = f acc >>= \(acc', s') ->
+                  (checkDone (check acc') . k $ Chunk s')
+    step acc k (Just ex) = throwRecoverableErr ex $ \str' ->
+      let i = f acc >>= \(acc', s') ->
+                           (checkDone (check acc') . k $ Chunk s')
+      in joinIM $ enumChunk str' i
 
 -- | Collapse a nested iteratee.  The inner iteratee is terminated by @EOF@.
 --   Errors are propagated through the result.
@@ -294,7 +382,7 @@
 -- |Applies the iteratee to the given stream.  This wraps 'enumEof',
 -- 'enumErr', and 'enumPure1Chunk', calling the appropriate enumerator
 -- based upon 'Stream'.
-enumChunk :: (Monad m, Monoid s) => Stream s -> Enumerator s m a
+enumChunk :: (Monad m) => Stream s -> Enumerator s m a
 enumChunk (Chunk xs)     = enumPure1Chunk xs
 enumChunk (EOF Nothing)  = enumEof
 enumChunk (EOF (Just e)) = enumErr e
@@ -371,10 +459,9 @@
 -- 
 -- It passes a given list of elements to the iteratee in one chunk
 -- This enumerator does no IO and is useful for testing of base parsing
-enumPure1Chunk :: (Monad m, Monoid s) => s -> Enumerator s m a
-enumPure1Chunk str iter = runIter iter onDone onCont
+enumPure1Chunk :: (Monad m) => s -> Enumerator s m a
+enumPure1Chunk str iter = runIter iter idoneM onCont
   where
-    onDone a str'    = idoneM a (Chunk str `mappend` str')
     onCont k Nothing = return $ k $ Chunk str
     onCont k e       = return $ icont k e
 
@@ -423,22 +510,24 @@
 
 -- |Create an enumerator from a callback function with an exception handler.
 -- The exception handler is called if an iteratee reports an exception.
-enumFromCallbackCatch ::
- (IException e, Monad m, NullPoint s) =>
-  (st -> m (Either SomeException ((Bool, st), s)))
+enumFromCallbackCatch
+  :: (IException e, Monad m, NullPoint s)
+  => (st -> m (Either SomeException ((Bool, st), s)))
   -> (e -> m (Maybe EnumException))
   -> st
   -> Enumerator s m a
 enumFromCallbackCatch c handler = loop
   where
     loop st iter = runIter iter idoneM (onCont st)
-    onCont st k Nothing = c st >>=
-        either (return . k . EOF . Just) (uncurry check)
-      where
-        check (b,st') = if b then loop st' . k . Chunk else return . k . Chunk
+    check k (True,  st') = loop st' . k . Chunk
+    check k (False,_st') = return . k . Chunk
+    onCont st k Nothing  = c st >>=
+        either (return . k . EOF . Just) (uncurry (check k))
     onCont st k j@(Just e) = case fromException e of
-      Just e' -> handler e' >>= maybe (loop st . k $ Chunk empty)
-                                 (return . icont k . Just) . fmap toException
+      Just e' -> handler e' >>=
+                   maybe (loop st . k $ Chunk empty)
+                         (return . icont k . Just) . fmap toException
       Nothing -> return (icont k j)
 {-# INLINE enumFromCallbackCatch #-}
+
 
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
@@ -30,6 +30,8 @@
   ,breakE
   ,take
   ,takeUpTo
+  ,takeWhile
+  ,takeWhileE
   ,mapStream
   ,rigidMapStream
   ,filter
@@ -64,7 +66,7 @@
 )
 where
 
-import Prelude hiding (mapM_, null, head, last, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product, zip, zip3, sequence_)
+import Prelude hiding (mapM_, null, head, last, drop, dropWhile, take, takeWhile, break, foldl, foldl1, length, filter, sum, product, zip, zip3, sequence_)
 
 import qualified Prelude as Prelude
 
@@ -412,7 +414,28 @@
 {-# SPECIALIZE takeUpTo :: Monad m => Int -> Enumeratee B.ByteString B.ByteString m a #-}
 {-# INLINABLE takeUpTo #-}
 
+-- | Takes an element predicate and returns the (possibly empty)
+-- prefix of the stream. All characters
+-- in the string will satisfy the character predicate. If the stream
+-- is not terminated, the first character of the
+-- remaining stream will not satisfy the predicate.
+-- 
+-- The analogue of @List.takeWhile@, see also @break@ and @takeWhileE@
+takeWhile :: (LL.ListLike s el, Monad m) => (el -> Bool) -> Iteratee s m s
+takeWhile = break . (not .)
+{-# INLINEABLE takeWhile #-}
 
+-- |Takes an element predicate and an iteratee, running the iteratee
+-- on all elements of the stream while the predicate is met.
+-- 
+-- This is preferred to @takeWhile@.
+takeWhileE
+ :: (Monad m, LL.ListLike s el, NullPoint s)
+ => (el -> Bool)
+ -> Enumeratee s s m a
+takeWhileE = breakE . (not .)
+{-# INLINEABLE takeWhileE #-}
+
 -- |Map the stream: another iteratee transformer
 -- Given the stream of elements of the type @el@ and the function @(el->el')@,
 -- build a nested stream of elements of the type @el'@ and apply the
@@ -481,9 +504,17 @@
     | otherwise               =
         let (full, rest) = gsplit . mconcat $ pfxd [s]
             pfxd'        = if LL.null rest then id else (rest:)
+            onDone x str = return $ Left (x,str)
+            onCont k Nothing = return . Right . Left . k $ Chunk full
+            onCont k e       = return . Right $ Right (liftI k, e)
         in  do
-              inext <- lift $ enumPure1Chunk full icur
-              liftI $ step (LL.length rest) pfxd' inext
+              res <- lift $ runIter icur onDone onCont
+              case res of
+                Left (x,str) -> idone (idone x str) (Chunk rest)
+                Right (Left inext)  -> liftI $ step (LL.length rest) pfxd' inext
+                Right (Right (inext, e)) -> icont (step (LL.length rest)
+                                                        pfxd' inext)
+                                                  e
   step _ pfxd icur mErr = case pfxd [] of
                          []   -> idone icur mErr
                          rest -> do
@@ -521,8 +552,19 @@
     -- accumulator.
     go icurr pfx (Chunk s) = case gsplit pfx s of
       ([], partial)   -> liftI $ go icurr partial
-      (full, partial) -> do inext <- lift $ enumPure1Chunk full icurr
-                            liftI $ go inext partial
+      (full, partial) -> do
+        -- if the inner iteratee is done, the outer iteratee needs to be
+        -- notified to terminate.
+        -- if the inner iteratee is in an error state, that error should
+        -- be lifted to the outer iteratee
+        let onCont k Nothing = return $ Right $ Left $ k $ Chunk full
+            onCont k e       = return $ Right $ Right (liftI k, e)
+            onDone x str     = return $ Left (x,str)
+        res <- lift $ runIter icurr onDone onCont
+        case res of
+          Left (x,str) -> idone (idone x str) (Chunk (mconcat $ snd partial []))
+          Right (Left inext)      -> liftI $ go inext partial
+          Right (Right (inext,e)) -> icont (go inext partial) e
     go icurr (_inpfx, pfxd) (EOF mex) = case pfxd [] of
       [] -> lift . enumChunk (EOF mex) $ icurr
       rest -> do inext <- lift . enumPure1Chunk [mconcat rest] $ icurr
@@ -750,7 +792,6 @@
   -> Iteratee s m (a, b)
 enumPair = zip
 
-
 -- |Enumerate two iteratees over a single stream simultaneously.
 -- 
 -- Compare to @List.zip@.
@@ -759,25 +800,38 @@
   => Iteratee s m a
   -> Iteratee s m b
   -> Iteratee s m (a, b)
-zip x y = liftI step
+zip x0 y0 = do
+    -- need to check if both iteratees are initially finished.  If so,
+    -- we don't want to push a chunk which will be dropped
+    (a', x') <- lift $ runIter x0 od oc
+    (b', y') <- lift $ runIter y0 od oc
+    case checkDone a' b' of
+      Just (Right (a,b,s))  -> idone (a,b) s  -- 's' may be EOF, needs to stay
+      Just (Left (Left a))  -> liftM (a,) y'
+      Just (Left (Right b)) -> liftM (,b) x'
+      Nothing               -> liftI (step x' y')
   where
-    step (Chunk xs) | nullC xs = liftI step
-    step (Chunk xs) = do
+    step x y (Chunk xs) | nullC xs = liftI (step x y)
+    step x y (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
+        Just (Right (a,b,s))  -> idone (a,b) s
+        Just (Left (Left a))  -> liftM (a,) y'
+        Just (Left (Right b)) -> liftM (,b) x'
+        Nothing               -> liftI (step x' y')
+    step x y (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)
+    checkDone r1 r2 = case (r1, r2) of
+      (Just (a, s1), Just (b,s2)) -> Just $ Right (a, b, shorter s1 s2)
+      (Just (a, _), Nothing)      -> Just . Left $ Left a
+      (Nothing, Just (b, _))      -> Just . Left $ Right b
+      (Nothing, Nothing)          -> Nothing
 
     shorter c1@(Chunk xs) c2@(Chunk ys)
       | LL.length xs < LL.length ys = c1
@@ -825,7 +879,17 @@
   => Iteratee s m a
   -> Iteratee s m b
   -> Iteratee s m (a, b)
-enumWith i1 i2 = go i1 i2
+enumWith i1 i2 = do
+    -- as with zip, first check to see if the initial iteratee is complete,
+    -- otherwise data would be dropped.
+    -- running the second iteratee as well to prevent a monadic effect mismatch
+    -- although I think that would be highly unlikely to happen in common
+    -- code
+    (a', x') <- lift $ runIter i1 od oc
+    (_,  y') <- lift $ runIter i2 od oc
+    case a' of
+      Just (a, s) -> flip idone s =<< lift (liftM (a,) $ run i2)
+      Nothing     -> go x' y'
   where
     od a s = return (Just (a, s), idone a s)
     oc k e = return (Nothing    , icont k e)
diff --git a/tests/QCUtils.hs b/tests/QCUtils.hs
--- a/tests/QCUtils.hs
+++ b/tests/QCUtils.hs
@@ -43,7 +43,11 @@
     ns <- arbitrary
     elements [
               I.drop n >> stream2list
+              ,I.drop n >> return ns
               ,I.break (< 5)
               ,I.heads ns >> stream2list
               ,I.peek >> stream2list
+              ,I.peek >> return ns
+              ,I.identity >> return []
+              ,I.identity >> return ns
               ]
diff --git a/tests/benchmarkHandle.hs b/tests/benchmarkHandle.hs
--- a/tests/benchmarkHandle.hs
+++ b/tests/benchmarkHandle.hs
@@ -9,6 +9,7 @@
 import Data.Monoid
 import Data.Word
 import Data.Iteratee
+import Data.Iteratee.Parallel
 import Data.Iteratee.Base.ReadableChunk
 import Data.Iteratee.IO.Fd (fileDriverFd)
 import Data.Iteratee.IO.Handle (fileDriverHandle)
diff --git a/tests/benchmarks.hs b/tests/benchmarks.hs
--- a/tests/benchmarks.hs
+++ b/tests/benchmarks.hs
@@ -6,6 +6,7 @@
 
 import Data.Iteratee
 import qualified Data.Iteratee.ListLike as I
+import qualified Data.Iteratee.Parallel as I
 import Data.Iteratee.ListLike (enumPureNChunk, stream2list, stream2stream)
 import Data.Word
 import Data.Monoid
@@ -76,6 +77,7 @@
 breakbench = makeGroup "break" $ break0 : break0' : breakBenches
 headsbench = makeGroup "heads" headsBenches
 dropbench = makeGroup "drop" $ drop0 : dropBenches
+zipbench = makeGroup "zip" $ zipBenches
 lengthbench = makeGroup "length" listBenches
 takebench = makeGroup "take" $ take0 : takeBenches
 takeUpTobench = makeGroup "takeUpTo" takeUpToBenches
@@ -90,6 +92,7 @@
 breakbenchbs = makeGroupBS "break" breakBenches
 headsbenchbs = makeGroupBS "heads" headsBenches
 dropbenchbs = makeGroupBS "drop" dropBenches
+zipbenchbs = makeGroupBS "zip" zipBenches
 lengthbenchbs = makeGroupBS "length" listBenches
 takebenchbs = makeGroupBS "take" takeBenches
 takeUpTobenchbs = makeGroupBS "takeUpTo" takeUpToBenches
@@ -100,9 +103,9 @@
 miscbenchbs = makeGroupBS "other" miscBenches
 
 
-allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, lengthbench, takebench, takeUpTobench, groupbench, mapbench, foldbench, convbench, miscbench]
+allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, zipbench, lengthbench, takebench, takeUpTobench, groupbench, mapbench, foldbench, convbench, miscbench]
 
-allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, lengthbenchbs, takebenchbs, takeUpTobenchbs, groupbenchbs, mapbenchbs, foldbenchbs, convbenchbs, miscbenchbs]
+allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, zipbenchbs, lengthbenchbs, takebenchbs, takeUpTobenchbs, groupbenchbs, mapbenchbs, foldbenchbs, convbenchbs, miscbenchbs]
 
 list0 = makeList "list one go" deepseq
 list1 = BDIter1 "stream2list one go" (flip deepseq ()) stream2list
@@ -146,6 +149,14 @@
 dropw3 = id1 "dropWhile small" (I.dropWhile ( < 100))
 dropw4 = id1 "dropWhile large" (I.dropWhile ( < 6000))
 dropBenches = [drop1, drop2, drop3, dropw1, dropw2, dropw3, dropw4]
+
+b_zip0 = idN "zip balanced" (I.zip (I.dropWhile (<100)) (I.dropWhile (<200))
+   >> identity)
+b_zip1 = idN "zip unbalanced" (I.zip (I.dropWhile (<8000)) (I.head) >> identity)
+b_zip2 = idN "zip unbalanced 2" (I.zip identity I.length >> identity)
+b_zip3 = idN "zip complete" (I.zip identity identity >> identity)
+b_zip4 = idN "zip nonterminating" (I.zip I.length I.stream2stream >> identity)
+zipBenches = [b_zip0, b_zip1, b_zip2, b_zip3, b_zip4 ]
 
 
 l1 = makeList "length of list" Prelude.length
diff --git a/tests/testIteratee.hs b/tests/testIteratee.hs
--- a/tests/testIteratee.hs
+++ b/tests/testIteratee.hs
@@ -150,17 +150,34 @@
 prop_length xs = runner1 (enumPure1Chunk xs Iter.length) == P.length xs
   where types = xs :: [Int]
 
+-- length 0 is an odd case.  enumPureNChunk skips null inputs, returning
+-- the original iteratee, which is then given to `enumEof` by `run`.
+-- This is different from enumPure1Chunk, which will provide a null chunk
+-- to the iteratee.
+-- 
+-- not certain ATM which should be correct...
+prop_chunkLength xs n = n > 0 ==>
+  runner1 (enumPureNChunk xs n (liftM2 (,) chunkLength stream2list))
+  == case P.length xs of
+       0              -> (Nothing, xs)
+       xl | xl >= n   -> (Just n, xs)
+          | otherwise -> (Just (P.length xs), xs)
+ where types = xs :: [Int]
+
+prop_chunkLength2 xs =
+  runner1 ((enumEof >=> enumPure1Chunk xs) chunkLength) == Nothing
+ where types = xs :: [Int]
+
+prop_takeFromChunk xs n k = n > 0 ==>
+  runner1 (enumPureNChunk xs n (liftM2 (,) (takeFromChunk k) stream2list))
+  == if k > n then splitAt n xs else splitAt k xs
+ where types = xs :: [Int]
+
 -- ---------------------------------------------
 -- Simple enumerator tests
 
 type I = Iteratee [Int] Identity [Int]
 
-prop_enumNoStream xs =
-  runIdentity (enumPure1Chunk xs (return 'a') >>= \i ->
-    Iter.run (i >> stream2list))
-  == xs
-  where types = xs :: [Int]
-
 prop_enumChunks n xs i = n > 0  ==>
   runner1 (enumPure1Chunk xs i) == runner1 (enumSpecial xs n i)
   where types = (n :: Int, xs :: [Int], i :: I)
@@ -195,6 +212,17 @@
                 == runner1 (enumPure1Chunk xs Iter.head)
   where types = xs :: [Int]
 
+prop_enumList xs i =
+  not (P.null xs) ==>
+  runner1 (enumList (replicate 100 xs) i)
+  == runner1 (enumPure1Chunk (concat $ replicate 100 xs) i)
+ where types = (xs :: [Int], i :: I)
+
+prop_enumCheckIfDone xs i =
+   runner1 (enumPure1Chunk xs (lift (enumCheckIfDone i) >>= snd))
+   == runner1 (enumPure1Chunk xs i)
+ where types = (xs :: [Int], i :: I)
+
 -- ---------------------------------------------
 -- Enumerator Combinators
 
@@ -206,7 +234,13 @@
    == runner1 (enumSpecial xs n $ Iter.dropWhile f >> stream2list)
  where types = (xs :: [Int])
 
+prop_enumWith3 xs i n =
+   n > 0
+   ==> runner1 (enumSpecial xs n $ enumWith i stream2list >> stream2list)
+   ==  runner1 (enumSpecial xs n (i >> stream2list))
+ where types = (xs :: [Int], i :: I)
 
+
 -- ---------------------------------------------
 -- Nested Iteratees
 
@@ -305,6 +339,11 @@
  == P.drop (min t d) xs
   where types = xs :: [Int]
 
+prop_takeWhile xs n f = n > 0 ==>
+  runner1 (enumSpecial xs n (liftM2 (,) (Iter.takeWhile f) stream2list))
+  == (P.takeWhile f xs, P.dropWhile f xs)
+ 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]
@@ -324,18 +363,53 @@
        == runner1 (enumPure1Chunk (List.groupBy pred xs) stream2list)
   where types = xs :: [Int]
 
+prop_mapChunksM xs n = n > 0 ==>
+ runWriter ((enumSpecial xs n (joinI $ Iter.mapChunksM f stream2list)) >>= run)
+ == (xs, Sum (P.length xs))
+  where f ck = tell (Sum $ P.length ck) >> return ck
+        types = xs :: [Int]
+{-
+prop_mapjoin xs i =
+  runIdentity (run (joinI . runIdentity $ enumPure1Chunk xs $ mapStream id i))
+  == runner1 (enumPure1Chunk xs i)
+  where types = (i :: I, xs :: [Int])
+-}
+
+prop_mapChunksM_ xs n = n > 0 ==>
+ snd (runWriter ((enumSpecial xs n (Iter.mapChunksM_ f)) >>= run))
+ == Sum (P.length xs)
+  where f ck = tell (Sum $ P.length ck)
+        types = xs :: [Int]
+
 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_foldChunksM xs x0 n = n > 0 ==>
+ runWriter ((enumSpecial xs n (Iter.foldChunksM f x0)) >>= run)
+ == runWriter (f x0 xs)
+  where f acc ck = CM.foldM f' acc ck
+        f' acc el = tell (Sum 1) >> return (acc+el)
+        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]
+-- ---------------------------------------------
+-- Zips
 
+prop_zip xs i1 i2 n = n > 0 ==>
+  runner1 (enumPureNChunk xs n $ liftM2 (,) (Iter.zip i1 i2) stream2list)
+  == let (r1, t1) = runner1 $ enumPure1Chunk xs $ liftM2 (,) i1 stream2list
+         (r2, t2) = runner1 $ enumPure1Chunk xs $ liftM2 (,) i2 stream2list
+         shorter = if P.length t1 > P.length t2 then t2 else t1
+     in ((r1,r2), shorter)
+ where types = (i1 :: I, i2 :: I, xs :: [Int])
+
 -- ---------------------------------------------
 -- Data.Iteratee.Char
 
@@ -375,6 +449,9 @@
     ,testProperty "last" prop_last1
     ,testProperty "last ends properly" prop_last2
     ,testProperty "length" prop_length
+    ,testProperty "chunkLength" prop_chunkLength
+    ,testProperty "chunkLength of EoF" prop_chunkLength2
+    ,testProperty "takeFromChunk" prop_takeFromChunk
     ,testProperty "drop" prop_drop
     ,testProperty "dropWhile" prop_dropWhile
     ,testProperty "skipToEof" prop_skip
@@ -385,8 +462,7 @@
     ,testProperty "iteratee Monad Assc" prop_iterMonad3
     ]
   ,testGroup "Simple Enumerators/Combinators" [
-    testProperty "enumPure1Chunk - final stream state" prop_enumNoStream
-    ,testProperty "enumPureNChunk" prop_enumChunks
+    testProperty "enumPureNChunk" prop_enumChunks
     ,testProperty "enum append 1" prop_app1
     ,testProperty "enum sequencing" prop_app2
     ,testProperty "enum sequencing 2" prop_app3
@@ -395,6 +471,8 @@
     ,testProperty "isFinished error" prop_isFinished2
     ,testProperty "null data idempotence" prop_null
     ,testProperty "null data head idempotence" prop_nullH
+    ,testProperty "enumList" prop_enumList
+    ,testProperty "enumCheckIfDone" prop_enumCheckIfDone
     ]
   ,testGroup "Nested iteratees" [
     testProperty "mapStream identity" prop_mapStream
@@ -408,6 +486,7 @@
     ,testProperty "takeUpTo" prop_takeUpTo
     ,testProperty "takeUpTo (finished iteratee)" prop_takeUpTo2
     ,testProperty "takeUpTo (remaining stream)" prop_takeUpTo3
+    ,testProperty "takeWhile" prop_takeWhile
     ,testProperty "filter" prop_filter
     ,testProperty "group" prop_group
     ,testProperty "groupBy" prop_groupBy
@@ -418,6 +497,7 @@
   ,testGroup "Enumerator Combinators" [
     testProperty "enumWith" prop_enumWith
     ,testProperty "enumWith remaining" prop_enumWith2
+    ,testProperty "enumWith remaining 2" prop_enumWith3
     ]
   ,testGroup "Folds" [
     testProperty "foldl" prop_foldl
@@ -427,12 +507,18 @@
    ,testProperty "sum" prop_sum
    ,testProperty "product" prop_product
    ]
+  ,testGroup "Zips" [
+    testProperty "zip" prop_zip
+   ]
   ,testGroup "Data.Iteratee.Char" [
     --testProperty "line" prop_line
     ]
   ,testGroup "Monadic functions" [
     testProperty "mapM_" prop_mapM_
    ,testProperty "foldM" prop_foldM
+   ,testProperty "mapChunksM" prop_mapChunksM
+   ,testProperty "mapChunksM_" prop_mapChunksM_
+   ,testProperty "foldChunksM" prop_foldChunksM
    ]
   ]
 
