diff --git a/CONTRIBUTORS b/CONTRIBUTORS
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -2,6 +2,7 @@
 
 Oleg Kiselyov
 Gregory Collins
+Nick Ingolia
 Brian Lewis
 John Lato
 Antoine Latter
diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.8.0.1
+version:       0.8.0.2
 synopsis:      Iteratee-based I/O
 description:
   The Iteratee monad provides strict, safe, and functional I/O. In addition
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
@@ -29,6 +29,8 @@
   ,mapStream
   ,rigidMapStream
   ,filter
+  ,group
+  ,groupBy
   -- ** Folds
   ,foldl
   ,foldl'
@@ -362,6 +364,59 @@
       | True       = idone (LL.filter p xs) mempty
     step _ = f'
 {-# INLINE filter #-}
+
+-- |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 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
+                          | otherwise    -> do inext <- lift $ enumPure1Chunk full icurr
+                                               liftI $ go inext partial
+        go icurr pfx (EOF mex) 
+          | LL.null pfx = lift . enumChunk (EOF mex) $ icurr
+          | otherwise = do inext <- lift $ enumPure1Chunk (LL.singleton pfx) icurr        
+                           lift . enumChunk (EOF mex) $ inext
+        gsplit ls = case LL.splitAt sz ls of
+          (g, rest) | LL.null rest -> if LL.length g == sz
+                                         then (LL.singleton g, LL.empty)
+                                         else (LL.empty, g)
+                    | otherwise -> let (grest, leftover) = gsplit rest
+                                       g' = g `LL.cons` grest
+                                   in g' `seq` (g', leftover)
+{-# INLINE group #-}
+
+-- |Creates an 'enumeratee' in which elements are grouped into
+-- contiguous blocks that are equal according to a predicate.
+-- 
+-- The analogue of @List.groupBy#
+          
+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)
+                                              | LL.null full -> liftI $ go icurr partial
+                                              | otherwise -> do inext <- lift . enumPure1Chunk full $ icurr
+                                                                liftI $ go inext partial
+          go icurr pfx (EOF mex) 
+            | LL.null pfx = lift . enumChunk (EOF mex) $ icurr
+            | otherwise = do inext <- lift . enumPure1Chunk (LL.singleton pfx) $ icurr
+                             lift . enumChunk (EOF mex) $ inext
+          gsplit ll | LL.null ll = (LL.empty, LL.empty)
+                    | otherwise = let groups = llGroupBy same ll
+                                      full = LL.init groups
+                                      partial = LL.last groups
+                                  in full `seq` partial `seq` (full, partial)
+          llGroupBy eq l -- Copied from Data.ListLike, avoid spurious (Eq el) constraint
+              | LL.null l = LL.empty
+              | otherwise = LL.cons (LL.cons x ys) (llGroupBy eq zs)
+              where (ys, zs) = LL.span (eq x) xs
+                    x = LL.head l
+                    xs = LL.tail l
+{-# INLINE groupBy #-}
 
 -- ------------------------------------------------------------------------
 -- Folds
diff --git a/tests/benchmarks.hs b/tests/benchmarks.hs
--- a/tests/benchmarks.hs
+++ b/tests/benchmarks.hs
@@ -77,8 +77,7 @@
 dropbench = makeGroup "drop" $ drop0 : dropBenches
 lengthbench = makeGroup "length" listBenches
 takebench = makeGroup "take" $ take0 : takeBenches
---takeRbench = makeGroup "takeR" $ takeR0 : takeRBenches
-takeRbench = makeGroup "takeR" []
+takeUpTobench = makeGroup "takeUpTo" takeUpToBenches
 mapbench = makeGroup "map" $ mapBenches
 convbench = makeGroup "convStream" convBenches
 miscbench = makeGroup "other" miscBenches
@@ -90,15 +89,15 @@
 dropbenchbs = makeGroupBS "drop" dropBenches
 lengthbenchbs = makeGroupBS "length" listBenches
 takebenchbs = makeGroupBS "take" takeBenches
-takeRbenchbs = makeGroupBS "takeR" takeRBenches
+takeUpTobenchbs = makeGroupBS "takeUpTo" takeUpToBenches
 mapbenchbs = makeGroupBS "map" mapBenches
 convbenchbs = makeGroupBS "convStream" convBenches
 miscbenchbs = makeGroupBS "other" miscBenches
 
 
-allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, lengthbench, takebench, takeRbench, mapbench, convbench, miscbench]
+allListBenches = bgroup "list" [listbench, streambench, breakbench, headsbench, dropbench, lengthbench, takebench, takeUpTobench, mapbench, convbench, miscbench]
 
-allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, lengthbenchbs, takebenchbs, takeRbenchbs, mapbenchbs, convbenchbs, miscbenchbs]
+allByteStringBenches = bgroup "bytestring" [listbenchbs, streambenchbs, breakbenchbs, headsbenchbs, dropbenchbs, lengthbenchbs, takebenchbs, takeUpTobenchbs, mapbenchbs, convbenchbs, miscbenchbs]
 
 list0 = makeList "list one go" deepseq
 list1 = BDIter1 "stream2list one go" (flip deepseq ()) stream2list
@@ -158,17 +157,13 @@
 take6 = idN "take length long chunked" (I.joinI $ I.take 1000 I.length)
 takeBenches = [take1, take2, take3, take4, take5, take6]
 
-{-
-takeR0 = makeList "take length of list long" (Prelude.length . Prelude.take 1000)
-takeR1 = id1 "takeR head short one go" (I.joinI $ I.take 20 I.head)
-takeR2 = id1 "takeR head long one go" (I.joinI $ I.takeR 1000 I.head)
-takeR3 = idN "takeR head short chunked" (I.joinI $ I.takeR 20 I.head)
-takeR4 = idN "takeR head long chunked" (I.joinI $ I.takeR 1000 I.head)
-takeR5 = id1 "takeR length long one go" (I.joinI $ I.takeR 1000 I.length)
-takeR6 = idN "takeR length long chunked" (I.joinI $ I.takeR 1000 I.length)
-takeRBenches = [takeR1, takeR2, takeR3, takeR4, takeR5, takeR6]
--}
-takeRBenches = []
+takeUpTo1 = id1 "takeUpTo head short one go" (I.joinI $ I.take 20 I.head)
+takeUpTo2 = id1 "takeUpTo head long one go" (I.joinI $ I.takeUpTo 1000 I.head)
+takeUpTo3 = idN "takeUpTo head short chunked" (I.joinI $ I.takeUpTo 20 I.head)
+takeUpTo4 = idN "takeUpTo head long chunked" (I.joinI $ I.takeUpTo 1000 I.head)
+takeUpTo5 = id1 "takeUpTo length long one go" (I.joinI $ I.takeUpTo 1000 I.length)
+takeUpTo6 = idN "takeUpTo length long chunked" (I.joinI $ I.takeUpTo 1000 I.length)
+takeUpToBenches = [takeUpTo1, takeUpTo2, takeUpTo3, takeUpTo4, takeUpTo5, takeUpTo6]
 
 map1 = id1 "map length one go" (I.joinI $ I.rigidMapStream id I.length)
 map2 = idN "map length chunked" (I.joinI $ I.rigidMapStream id I.length)
diff --git a/tests/testIteratee.hs b/tests/testIteratee.hs
--- a/tests/testIteratee.hs
+++ b/tests/testIteratee.hs
@@ -14,6 +14,7 @@
 import qualified Data.Iteratee.Char as IC
 import qualified Data.Iteratee as Iter
 import Data.Functor.Identity
+import qualified Data.List as List (groupBy, unfoldr)
 import Data.Monoid
 import qualified Data.ListLike as LL
 
@@ -216,6 +217,21 @@
                   == runner2 (enumPure1Chunk xs $ takeUpTo n stream2list)
   where types = xs :: [Int]
 
+prop_group xs n = n > 0 ==>
+                  runner2 (enumPure1Chunk xs $ Iter.group n stream2list)
+                  == runner1 (enumPure1Chunk groups stream2list)
+  where types = xs :: [Int]
+        groups :: [[Int]]
+        groups = List.unfoldr groupOne xs
+          where groupOne [] = Nothing
+                groupOne elts@(_:_) = Just . splitAt n $ elts
+                           
+prop_groupBy xs m = m > 0 ==>
+                  runner2 (enumPure1Chunk xs $ Iter.groupBy pred stream2list)
+                  == runner1 (enumPure1Chunk (List.groupBy pred xs) stream2list)
+  where types = xs :: [Int]
+        pred z1 z2 = (z1 `mod` m == z2 `mod` m)
+
 -- ---------------------------------------------
 -- Data.Iteratee.Char
 
@@ -276,6 +292,8 @@
     ,testProperty "take" prop_take
     ,testProperty "take (finished iteratee)" prop_take2
     ,testProperty "takeUpTo" prop_takeUpTo
+    ,testProperty "group" prop_group
+    ,testProperty "groupBy" prop_groupBy
     ,testProperty "convStream EOF" prop_convstream2
     ,testProperty "convStream identity" prop_convstream
     ,testProperty "convStream identity 2" prop_convstream3
