diff --git a/enumerator.cabal b/enumerator.cabal
--- a/enumerator.cabal
+++ b/enumerator.cabal
@@ -1,5 +1,5 @@
 name: enumerator
-version: 0.4.10
+version: 0.4.11
 synopsis: Reliable, high-performance processing with left-fold enumerators
 license: MIT
 license-file: license.txt
@@ -93,6 +93,7 @@
   build-depends:
       transformers >= 0.2 && < 0.3
     , bytestring >= 0.9 && < 0.10
+    , containers
     , text >= 0.7 && < 0.12
 
   if impl(ghc >= 6.10)
diff --git a/hs/Data/Enumerator/Binary.hs b/hs/Data/Enumerator/Binary.hs
--- a/hs/Data/Enumerator/Binary.hs
+++ b/hs/Data/Enumerator/Binary.hs
@@ -42,12 +42,15 @@
 	-- ** Maps
 	, Data.Enumerator.Binary.map
 	, Data.Enumerator.Binary.mapM
+	, Data.Enumerator.Binary.mapM_
 	, Data.Enumerator.Binary.concatMap
 	, concatMapM
 	
 	-- ** Accumulating maps
 	, mapAccum
 	, mapAccumM
+	, concatMapAccum
+	, concatMapAccumM
 	
 	-- ** Infinite streams
 	, Data.Enumerator.Binary.iterate
@@ -82,7 +85,7 @@
 
 	) where
 
-import Prelude hiding (head, drop, takeWhile)
+import Prelude hiding (head, drop, takeWhile, mapM_)
 import Data.Enumerator hiding ( head, drop, iterateM, repeatM, replicateM
                               , generateM, filterM, consume, foldM
                               , concatMapM)
@@ -166,6 +169,14 @@
 mapM f = Data.Enumerator.Binary.concatMapM (\x -> liftM B.singleton (f x))
 
 
+-- | @'mapM_' f@ applies /f/ to each input byte, and discards the results.
+--
+-- Since: 0.4.11
+
+mapM_ :: Monad m => (Word8 -> m ()) -> Iteratee B.ByteString m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
+
+
 -- | @'concatMap' f@ applies /f/ to each input byte and feeds the
 -- resulting outputs to the inner iteratee.
 --
@@ -192,12 +203,12 @@
 			checkDoneEx (Chunks [B.pack xs]) (\k' -> loop k' xs)
 
 
--- | Similar to 'map', but with a stateful step function.
+-- | Similar to 'concatMap', but with a stateful step function.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
-mapAccum f s0 = checkDone (continue . step s0) where
+concatMapAccum :: Monad m => (s -> Word8 -> (s, B.ByteString)) -> s -> Enumeratee B.ByteString B.ByteString m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -205,16 +216,16 @@
 	loop s k (x:xs) = case B.uncons x of
 		Nothing -> loop s k xs
 		Just (b, x') -> case f s b of
-			(s', ai) -> k (Chunks [B.singleton ai]) >>==
+			(s', ai) -> k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
 
--- | Similar to 'mapM', but with a stateful step function.
+-- | Similar to 'concatMapM', but with a stateful step function.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+concatMapAccumM :: Monad m => (s -> Word8 -> m (s, B.ByteString)) -> s -> Enumeratee B.ByteString B.ByteString m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -223,10 +234,28 @@
 		Nothing -> loop s k xs
 		Just (b, x') -> do
 			(s', ai) <- lift (f s b)
-			k (Chunks [B.singleton ai]) >>==
+			k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
 
+-- | Similar to 'map', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
+mapAccum f = concatMapAccum (\s w -> case f s w of (s', w') -> (s', B.singleton w'))
+
+
+-- | Similar to 'mapM', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
+mapAccumM f = concatMapAccumM (\s w -> do
+	(s', w') <- f s w
+	return (s', B.singleton w'))
+
+
 -- | @'iterate' f x@ enumerates an infinite stream of repeated applications
 -- of /f/ to /x/.
 --
@@ -592,7 +621,7 @@
 	step EOF = yield () EOF
 	step (Chunks []) = continue step
 	step (Chunks bytes) = do
-		tryIO (mapM_ (B.hPut h) bytes)
+		tryIO (CM.mapM_ (B.hPut h) bytes)
 		continue step
 
 
diff --git a/hs/Data/Enumerator/List.hs b/hs/Data/Enumerator/List.hs
--- a/hs/Data/Enumerator/List.hs
+++ b/hs/Data/Enumerator/List.hs
@@ -30,12 +30,15 @@
 	-- ** Maps
 	, Data.Enumerator.List.map
 	, Data.Enumerator.List.mapM
+	, Data.Enumerator.List.mapM_
 	, Data.Enumerator.List.concatMap
 	, concatMapM
 	
 	-- ** Accumulating maps
 	, mapAccum
 	, mapAccumM
+	, concatMapAccum
+	, concatMapAccumM
 	
 	-- ** Infinite streams
 	, Data.Enumerator.List.iterate
@@ -66,6 +69,7 @@
 	, require
 	, isolate
 	, splitWhen
+	, unique
 
 	) where
 
@@ -76,6 +80,7 @@
 import qualified Control.Monad as CM
 import qualified Data.List as L
 import Control.Exception (ErrorCall(..))
+import qualified Data.Set
 
 
 
@@ -186,37 +191,63 @@
 mapM f = concatMapM (\x -> Prelude.mapM f [x])
 
 
--- | Similar to 'map', but with a stateful step function.
+-- | @'mapM_' f@ applies /f/ to each input element, and discards the results.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b
-mapAccum f s0 = checkDone (continue . step s0) where
+mapM_ :: Monad m => (a -> m b) -> Iteratee a m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
+
+
+-- | Similar to 'concatMap', but with a stateful step function.
+--
+-- Since: 0.4.11
+
+concatMapAccum :: Monad m => (s -> ao -> (s, [ai])) -> s -> Enumeratee ao ai m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
 	loop s k [] = continue (step s k)
 	loop s k (x:xs) = case f s x of
-		(s', ai) -> k (Chunks [ai]) >>==
+		(s', ai) -> k (Chunks ai) >>==
 			checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)
 
 
--- | Similar to 'mapM', but with a stateful step function.
+-- | Similar to 'concatMapM', but with a stateful step function.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+concatMapAccumM :: Monad m => (s -> ao -> m (s, [ai])) -> s -> Enumeratee ao ai m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
 	loop s k [] = continue (step s k)
 	loop s k (x:xs) = do
 		(s', ai) <- lift (f s x)
-		k (Chunks [ai]) >>==
+		k (Chunks ai) >>==
 			checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)
 
 
+-- | Similar to 'map', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b
+mapAccum f = concatMapAccum (\s ao -> case f s ao of (s', ai) -> (s', [ai]))
+
+
+-- | Similar to 'mapM', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b
+mapAccumM f = concatMapAccumM (\s ao -> do
+	(s', ai) <- f s ao
+	return (s', [ai]))
+
+
 -- | @'iterate' f x@ enumerates an infinite stream of repeated applications
 -- of /f/ to /x/.
 --
@@ -447,3 +478,18 @@
 	as <- takeWhile (not . p)
 	drop 1
 	return as
+
+
+-- | Remove duplicate elements from a stream, passing through the first
+-- instance of each value.
+--
+-- Similar to 'nub', but more efficient because it uses a 'Data.Set.Set'
+-- internally.
+--
+-- Since: 0.4.11
+
+unique :: (Ord a, Monad m) => Enumeratee a a m b
+unique = concatMapAccum step Data.Set.empty where
+	step s x = if Data.Set.member x s
+		then (s, [])
+		else (Data.Set.insert x s, [x])
diff --git a/hs/Data/Enumerator/Text.hs b/hs/Data/Enumerator/Text.hs
--- a/hs/Data/Enumerator/Text.hs
+++ b/hs/Data/Enumerator/Text.hs
@@ -40,12 +40,15 @@
 	-- ** Maps
 	, Data.Enumerator.Text.map
 	, Data.Enumerator.Text.mapM
+	, Data.Enumerator.Text.mapM_
 	, Data.Enumerator.Text.concatMap
 	, concatMapM
 	
 	-- ** Accumulating maps
 	, mapAccum
 	, mapAccumM
+	, concatMapAccum
+	, concatMapAccumM
 	
 	-- ** Infinite streams
 	, Data.Enumerator.Text.iterate
@@ -187,6 +190,15 @@
 mapM f = Data.Enumerator.Text.concatMapM (\x -> liftM T.singleton (f x))
 
 
+-- | @'mapM_' f@ applies /f/ to each input character, and discards the
+-- results.
+--
+-- Since: 0.4.11
+
+mapM_ :: Monad m => (Char -> m ()) -> Iteratee T.Text m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
+
+
 -- | @'concatMap' f@ applies /f/ to each input character and feeds the
 -- resulting outputs to the inner iteratee.
 --
@@ -213,12 +225,12 @@
 			checkDoneEx (Chunks [T.pack xs]) (\k' -> loop k' xs)
 
 
--- | Similar to 'map', but with a stateful step function.
+-- | Similar to 'concatMap', but with a stateful step function.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b
-mapAccum f s0 = checkDone (continue . step s0) where
+concatMapAccum :: Monad m => (s -> Char -> (s, T.Text)) -> s -> Enumeratee T.Text T.Text m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -226,16 +238,16 @@
 	loop s k (x:xs) = case T.uncons x of
 		Nothing -> loop s k xs
 		Just (c, x') -> case f s c of
-			(s', ai) -> k (Chunks [T.singleton ai]) >>==
+			(s', ai) -> k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
 
--- | Similar to 'mapM', but with a stateful step function.
+-- | Similar to 'concatMapM', but with a stateful step function.
 --
--- Since: 0.4.9
+-- Since: 0.4.11
 
-mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+concatMapAccumM :: Monad m => (s -> Char -> m (s, T.Text)) -> s -> Enumeratee T.Text T.Text m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -244,10 +256,28 @@
 		Nothing -> loop s k xs
 		Just (c, x') -> do
 			(s', ai) <- lift (f s c)
-			k (Chunks [T.singleton ai]) >>==
+			k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
 
+-- | Similar to 'map', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b
+mapAccum f = concatMapAccum (\s c -> case f s c of (s', c') -> (s', T.singleton c'))
+
+
+-- | Similar to 'mapM', but with a stateful step function.
+--
+-- Since: 0.4.9
+
+mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b
+mapAccumM f = concatMapAccumM (\s c -> do
+	(s', c') <- f s c
+	return (s', T.singleton c'))
+
+
 -- | @'iterate' f x@ enumerates an infinite stream of repeated applications
 -- of /f/ to /x/.
 --
@@ -548,7 +578,7 @@
 	step EOF = yield () EOF
 	step (Chunks []) = continue step
 	step (Chunks chunks) = do
-		tryIO (mapM_ (TIO.hPutStr h) chunks)
+		tryIO (CM.mapM_ (TIO.hPutStr h) chunks)
 		continue step
 
 
diff --git a/src/api-docs.anansi b/src/api-docs.anansi
--- a/src/api-docs.anansi
+++ b/src/api-docs.anansi
@@ -717,6 +717,12 @@
 -- Since: 0.4.8
 :
 
+:d apidoc Data.Enumerator.Binary.mapM_
+-- | @'mapM_' f@ applies /f/ to each input byte, and discards the results.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.Binary.mapAccum
 -- | Similar to 'map', but with a stateful step function.
 --
@@ -729,6 +735,18 @@
 -- Since: 0.4.9
 :
 
+:d apidoc Data.Enumerator.Binary.concatMapAccum
+-- | Similar to 'concatMap', but with a stateful step function.
+--
+-- Since: 0.4.11
+:
+
+:d apidoc Data.Enumerator.Binary.concatMapAccumM
+-- | Similar to 'concatMapM', but with a stateful step function.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.Binary.repeat
 -- | Enumerates an infinite stream of a single byte.
 --
@@ -925,6 +943,12 @@
 -- Since: 0.4.8
 :
 
+:d apidoc Data.Enumerator.List.mapM_
+-- | @'mapM_' f@ applies /f/ to each input element, and discards the results.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.List.mapAccum
 -- | Similar to 'map', but with a stateful step function.
 --
@@ -937,6 +961,18 @@
 -- Since: 0.4.9
 :
 
+:d apidoc Data.Enumerator.List.concatMapAccum
+-- | Similar to 'concatMap', but with a stateful step function.
+--
+-- Since: 0.4.11
+:
+
+:d apidoc Data.Enumerator.List.concatMapAccumM
+-- | Similar to 'concatMapM', but with a stateful step function.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.List.repeat
 -- | Enumerates an infinite stream of a single element.
 --
@@ -1011,6 +1047,16 @@
 -- Since: 0.4.8
 :
 
+:d apidoc Data.Enumerator.List.unique
+-- | Remove duplicate elements from a stream, passing through the first
+-- instance of each value.
+--
+-- Similar to 'nub', but more efficient because it uses a 'Data.Set.Set'
+-- internally.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.Text.Codec
 -- | Since: 0.2
 :
@@ -1176,6 +1222,13 @@
 -- Since: 0.4.8
 :
 
+:d apidoc Data.Enumerator.Text.mapM_
+-- | @'mapM_' f@ applies /f/ to each input character, and discards the
+-- results.
+--
+-- Since: 0.4.11
+:
+
 :d apidoc Data.Enumerator.Text.mapAccum
 -- | Similar to 'map', but with a stateful step function.
 --
@@ -1186,6 +1239,18 @@
 -- | Similar to 'mapM', but with a stateful step function.
 --
 -- Since: 0.4.9
+:
+
+:d apidoc Data.Enumerator.Text.concatMapAccum
+-- | Similar to 'concatMap', but with a stateful step function.
+--
+-- Since: 0.4.11
+:
+
+:d apidoc Data.Enumerator.Text.concatMapAccumM
+-- | Similar to 'concatMapM', but with a stateful step function.
+--
+-- Since: 0.4.11
 :
 
 :d apidoc Data.Enumerator.Text.repeat
diff --git a/src/enumerator.anansi b/src/enumerator.anansi
--- a/src/enumerator.anansi
+++ b/src/enumerator.anansi
@@ -30,10 +30,10 @@
 
 \newcommand{\io}{{\sc i/o}}
 
-\title{enumerator\_0.4.10}
+\title{enumerator\_0.4.9}
 \author{John Millikin\\
         \href{mailto:"John Millikin" <jmillikin@gmail.com>}{\tt jmillikin@gmail.com}}
-\date{April 26, 2011}
+\date{March 29, 2011}
 
 \begin{document}
 
diff --git a/src/io.anansi b/src/io.anansi
--- a/src/io.anansi
+++ b/src/io.anansi
@@ -93,7 +93,7 @@
 	step EOF = yield () EOF
 	step (Chunks []) = continue step
 	step (Chunks bytes) = do
-		tryIO (mapM_ (B.hPut h) bytes)
+		tryIO (CM.mapM_ (B.hPut h) bytes)
 		continue step
 :
 
@@ -140,6 +140,6 @@
 	step EOF = yield () EOF
 	step (Chunks []) = continue step
 	step (Chunks chunks) = do
-		tryIO (mapM_ (TIO.hPutStr h) chunks)
+		tryIO (CM.mapM_ (TIO.hPutStr h) chunks)
 		continue step
 :
diff --git a/src/list-analogues.anansi b/src/list-analogues.anansi
--- a/src/list-analogues.anansi
+++ b/src/list-analogues.anansi
@@ -160,6 +160,10 @@
 mapM :: Monad m => (ao -> m ai)
      -> Enumeratee ao ai m b
 mapM f = concatMapM (\x -> Prelude.mapM f [x])
+
+|apidoc Data.Enumerator.List.mapM_|
+mapM_ :: Monad m => (a -> m b) -> Iteratee a m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
 :
 
 :d byte-oriented list analogues
@@ -171,6 +175,10 @@
 mapM :: Monad m => (Word8 -> m Word8) -> Enumeratee B.ByteString B.ByteString m b
 mapM f = Data.Enumerator.Binary.concatMapM (\x -> liftM B.singleton (f x))
 
+|apidoc Data.Enumerator.Binary.mapM_|
+mapM_ :: Monad m => (Word8 -> m ()) -> Iteratee B.ByteString m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
+
 |apidoc Data.Enumerator.Binary.concatMap|
 concatMap :: Monad m => (Word8 -> B.ByteString) -> Enumeratee B.ByteString B.ByteString m b
 concatMap f = Data.Enumerator.Binary.concatMapM (return . f)
@@ -197,6 +205,10 @@
 mapM :: Monad m => (Char -> m Char) -> Enumeratee T.Text T.Text m b
 mapM f = Data.Enumerator.Text.concatMapM (\x -> liftM T.singleton (f x))
 
+|apidoc Data.Enumerator.Text.mapM_|
+mapM_ :: Monad m => (Char -> m ()) -> Iteratee T.Text m ()
+mapM_ f = foldM (\_ x -> f x >> return ()) ()
+
 |apidoc Data.Enumerator.Text.concatMap|
 concatMap :: Monad m => (Char -> T.Text) -> Enumeratee T.Text T.Text m b
 concatMap f = Data.Enumerator.Text.concatMapM (return . f)
@@ -217,34 +229,44 @@
 \subsection{Accumulating maps}
 
 :d element-oriented list analogues
-|apidoc Data.Enumerator.List.mapAccum|
-mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b
-mapAccum f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.List.concatMapAccum|
+concatMapAccum :: Monad m => (s -> ao -> (s, [ai])) -> s -> Enumeratee ao ai m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
 	loop s k [] = continue (step s k)
 	loop s k (x:xs) = case f s x of
-		(s', ai) -> k (Chunks [ai]) >>==
+		(s', ai) -> k (Chunks ai) >>==
 			checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)
 
-|apidoc Data.Enumerator.List.mapAccumM|
-mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.List.concatMapAccumM|
+concatMapAccumM :: Monad m => (s -> ao -> m (s, [ai])) -> s -> Enumeratee ao ai m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
 	loop s k [] = continue (step s k)
 	loop s k (x:xs) = do
 		(s', ai) <- lift (f s x)
-		k (Chunks [ai]) >>==
+		k (Chunks ai) >>==
 			checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)
+
+|apidoc Data.Enumerator.List.mapAccum|
+mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b
+mapAccum f = concatMapAccum (\s ao -> case f s ao of (s', ai) -> (s', [ai]))
+
+|apidoc Data.Enumerator.List.mapAccumM|
+mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b
+mapAccumM f = concatMapAccumM (\s ao -> do
+	(s', ai) <- f s ao
+	return (s', [ai]))
 :
 
 :d byte-oriented list analogues
-|apidoc Data.Enumerator.Binary.mapAccum|
-mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
-mapAccum f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.Binary.concatMapAccum|
+concatMapAccum :: Monad m => (s -> Word8 -> (s, B.ByteString)) -> s -> Enumeratee B.ByteString B.ByteString m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -252,12 +274,12 @@
 	loop s k (x:xs) = case B.uncons x of
 		Nothing -> loop s k xs
 		Just (b, x') -> case f s b of
-			(s', ai) -> k (Chunks [B.singleton ai]) >>==
+			(s', ai) -> k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
-|apidoc Data.Enumerator.Binary.mapAccumM|
-mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.Binary.concatMapAccumM|
+concatMapAccumM :: Monad m => (s -> Word8 -> m (s, B.ByteString)) -> s -> Enumeratee B.ByteString B.ByteString m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -266,14 +288,24 @@
 		Nothing -> loop s k xs
 		Just (b, x') -> do
 			(s', ai) <- lift (f s b)
-			k (Chunks [B.singleton ai]) >>==
+			k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
+
+|apidoc Data.Enumerator.Binary.mapAccum|
+mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
+mapAccum f = concatMapAccum (\s w -> case f s w of (s', w') -> (s', B.singleton w'))
+
+|apidoc Data.Enumerator.Binary.mapAccumM|
+mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b
+mapAccumM f = concatMapAccumM (\s w -> do
+	(s', w') <- f s w
+	return (s', B.singleton w'))
 :
 
 :d text-oriented list analogues
-|apidoc Data.Enumerator.Text.mapAccum|
-mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b
-mapAccum f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.Text.concatMapAccum|
+concatMapAccum :: Monad m => (s -> Char -> (s, T.Text)) -> s -> Enumeratee T.Text T.Text m b
+concatMapAccum f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -281,12 +313,12 @@
 	loop s k (x:xs) = case T.uncons x of
 		Nothing -> loop s k xs
 		Just (c, x') -> case f s c of
-			(s', ai) -> k (Chunks [T.singleton ai]) >>==
+			(s', ai) -> k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
 
-|apidoc Data.Enumerator.Text.mapAccumM|
-mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b
-mapAccumM f s0 = checkDone (continue . step s0) where
+|apidoc Data.Enumerator.Text.concatMapAccumM|
+concatMapAccumM :: Monad m => (s -> Char -> m (s, T.Text)) -> s -> Enumeratee T.Text T.Text m b
+concatMapAccumM f s0 = checkDone (continue . step s0) where
 	step _ k EOF = yield (Continue k) EOF
 	step s k (Chunks xs) = loop s k xs
 	
@@ -295,8 +327,18 @@
 		Nothing -> loop s k xs
 		Just (c, x') -> do
 			(s', ai) <- lift (f s c)
-			k (Chunks [T.singleton ai]) >>==
+			k (Chunks [ai]) >>==
 				checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))
+
+|apidoc Data.Enumerator.Text.mapAccum|
+mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b
+mapAccum f = concatMapAccum (\s c -> case f s c of (s', c') -> (s', T.singleton c'))
+
+|apidoc Data.Enumerator.Text.mapAccumM|
+mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b
+mapAccumM f = concatMapAccumM (\s c -> do
+	(s', c') <- f s c
+	return (s', T.singleton c'))
 :
 
 \subsection{Infinite streams}
@@ -871,4 +913,13 @@
 |apidoc Data.Enumerator.Text.lines|
 lines :: Monad m => Enumeratee T.Text T.Text m b
 lines = splitWhen (== '\n')
+:
+
+:d element-oriented list analogues
+|apidoc Data.Enumerator.List.unique|
+unique :: (Ord a, Monad m) => Enumeratee a a m b
+unique = concatMapAccum step Data.Set.empty where
+	step s x = if Data.Set.member x s
+		then (s, [])
+		else (Data.Set.insert x s, [x])
 :
diff --git a/src/public-interface.anansi b/src/public-interface.anansi
--- a/src/public-interface.anansi
+++ b/src/public-interface.anansi
@@ -66,7 +66,7 @@
 :
 
 :d Data.Enumerator.Binary imports
-import Prelude hiding (head, drop, takeWhile)
+import Prelude hiding (head, drop, takeWhile, mapM_)
 import Data.Enumerator hiding ( head, drop, iterateM, repeatM, replicateM
                               , generateM, filterM, consume, foldM
                               , concatMapM)
@@ -91,6 +91,7 @@
 import qualified Control.Monad as CM
 import qualified Data.List as L
 import Control.Exception (ErrorCall(..))
+import qualified Data.Set
 :
 
 :d Data.Enumerator.Text imports
@@ -223,12 +224,15 @@
 -- ** Maps
 , Data.Enumerator.Binary.map
 , Data.Enumerator.Binary.mapM
+, Data.Enumerator.Binary.mapM_
 , Data.Enumerator.Binary.concatMap
 , concatMapM
 
 -- ** Accumulating maps
 , mapAccum
 , mapAccumM
+, concatMapAccum
+, concatMapAccumM
 
 -- ** Infinite streams
 , Data.Enumerator.Binary.iterate
@@ -272,12 +276,15 @@
 -- ** Maps
 , Data.Enumerator.List.map
 , Data.Enumerator.List.mapM
+, Data.Enumerator.List.mapM_
 , Data.Enumerator.List.concatMap
 , concatMapM
 
 -- ** Accumulating maps
 , mapAccum
 , mapAccumM
+, concatMapAccum
+, concatMapAccumM
 
 -- ** Infinite streams
 , Data.Enumerator.List.iterate
@@ -308,6 +315,7 @@
 , require
 , isolate
 , splitWhen
+, unique
 :
 
 :d Data.Enumerator.Text exports
@@ -325,12 +333,15 @@
 -- ** Maps
 , Data.Enumerator.Text.map
 , Data.Enumerator.Text.mapM
+, Data.Enumerator.Text.mapM_
 , Data.Enumerator.Text.concatMap
 , concatMapM
 
 -- ** Accumulating maps
 , mapAccum
 , mapAccumM
+, concatMapAccum
+, concatMapAccumM
 
 -- ** Infinite streams
 , Data.Enumerator.Text.iterate
