diff --git a/CONTRIBUTORS b/CONTRIBUTORS
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,9 +1,11 @@
 Thanks to the following individuals for contributing to this project.
 
+Brian Buecking
 Oleg Kiselyov
 Brian Lewis
 John Lato
 Echo Nolan
+Conrad Parker
 Paulo Tanimoto
 Magnus Therning
 Johan Tibell
diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.3.5
+version:       0.3.6
 synopsis:      Iteratee-based I/O
 description:
   The IterateeGM monad provides strict, safe, and functional I/O. In addition
@@ -10,7 +10,7 @@
 license:       BSD3
 license-file:  LICENSE
 homepage:      http://inmachina.net/~jwlato/haskell/iteratee
-tested-with:   GHC == 6.10.4
+tested-with:   GHC == 6.12.1
 stability:     experimental
 
 cabal-version: >= 1.6
@@ -62,7 +62,6 @@
     bytestring            >= 0.9   && < 0.10,
     containers            >= 0.2   && < 0.4,
     extensible-exceptions >= 0.1   && < 0.2,
-    haskell98             >= 1     && < 2,
     transformers          >= 0.2.0.0 && < 0.3
 
   exposed-modules:
@@ -74,6 +73,7 @@
     Data.Iteratee.Char
     Data.Iteratee.IO
     Data.Iteratee.IO.Handle
+    Data.Iteratee.IO.Interact
     Data.Iteratee.WrappedByteString
 
   if flag(includeCodecs)
@@ -101,7 +101,7 @@
   if flag(buildTests)
     build-depends:
       QuickCheck                 >= 2   && < 3,
-      test-framework             >= 0.2 && < 0.3,
+      test-framework             >= 0.3 && < 0.4,
       test-framework-quickcheck2 >= 0.2 && < 0.3
   else
     executable: False
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
@@ -35,6 +35,7 @@
   head,
   heads,
   peek,
+  last,
   skipToEof,
   length,
   -- ** Nested iteratee combinators
@@ -44,6 +45,7 @@
   rigidMapStream,
   looseMapStream,
   convStream,
+  convStateStream,
   filter,
   -- ** Folds
   foldl,
@@ -52,6 +54,8 @@
   -- ** Special Folds
   sum,
   product,
+  -- ** Monadic variants of iteratees
+  mapM_,
   -- * Enumerators
   -- ** Basic enumerators
   enumEof,
@@ -69,7 +73,7 @@
 )
 where
 
-import Prelude hiding (head, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product)
+import Prelude hiding (head, last, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product, mapM_)
 import qualified Prelude as P
 
 import qualified Data.Iteratee.Base.StreamChunk as SC
@@ -77,7 +81,7 @@
 import qualified Data.ListLike.FoldableLL as FLL
 import Data.Iteratee.Base.LooseMap
 import Data.Iteratee.IO.Base
-import Control.Monad
+import Control.Monad hiding (mapM_)
 import Control.Applicative
 import Control.Monad.Trans.Class
 import Control.Monad.IO.Class
@@ -86,7 +90,7 @@
 
 
 -- |A stream is a (continuing) sequence of elements bundled in Chunks.
--- The first two variants indicate termination of the stream.
+-- The first variant indicates termination of the stream.
 -- Chunk a gives the currently available part of the stream.
 -- The stream is not terminated yet.
 -- The case (null Chunk) signifies a stream with no currently available
@@ -397,6 +401,13 @@
     | otherwise = return $ Done (Just $ SC.head vec) s
   step stream   = return $ Done Nothing stream
 
+-- | Attempt to skip to the last element of the stream and return it
+last :: (SC.StreamChunk s el, Monad m) => IterateeG s el m el
+last = do x <- head
+          next <- peek
+          case next of
+              Just _  -> last
+              Nothing -> return x
 
 -- |Skip the rest of the stream
 skipToEof :: (Monad m) => IterateeG s el m ()
@@ -573,6 +584,38 @@
 
 {-# INLINE convStream #-}
 
+-- |Convert one stream into another while continually updating an internal
+-- state. The state of type 't' is updated by the supplied function, which
+-- maybe returns a tuple consisting of the updated state, the remaining
+-- unprocessed portion of the input stream, and the output stream.
+-- In order to produce elements of the output stream from data that spans
+-- stream chunks, the remaining portion of the input stream is passed to the
+-- following iteration of the supplied function, which should prepend it to
+-- its input stream chunk.
+-- The supplied function should return Nothing on EOF.
+convStateStream :: MonadIO m =>
+  (t -> s el -> IterateeG s el m (Maybe (t, s el, s' el'))) ->
+  t -> s el ->
+  EnumeratorN s el s' el' m b
+convStateStream outer state pre inner = outer state pre >>= convStateCheck outer inner
+
+{-# INLINE convStateStream #-}
+
+convStateCheck :: (MonadIO m) =>
+     (t -> s el -> IterateeG s el m (Maybe (t, s el, s' el')))
+     -> IterateeG s' el' m b
+     -> Maybe (t, s el, s' el')
+     -> IterateeG s el m (IterateeG s' el' m b)
+convStateCheck outer inner (Just (state', remainder, result)) =
+  lift (runIter inner (Chunk result)) >>= docase
+  where
+    docase (Done a _) = return . return $ a
+    docase (Cont k Nothing)  = convStateStream outer state' remainder k
+    docase (Cont _ (Just e)) = return $ throwErr e
+convStateCheck _ iter (Nothing) = return iter
+
+{-# INLINE convStateCheck #-}
+
 -- |Creates an enumerator with only elements from the stream that
 -- satisfy the predicate function.
 filter :: (LL.ListLike (s el) el, Monad m) =>
@@ -751,3 +794,18 @@
 -- |A variant of join for Iteratees in a monad.
 joinIM :: (Monad m) => m (IterateeG s el m a) -> IterateeG s el m a
 joinIM m = IterateeG (\str -> m >>= flip runIter str)
+
+-- ------------------------------------------------------------------------
+-- Monadic variants of iteratees
+
+-- | Map a monadic function over all elements of a stream, and ignore the result
+mapM_ :: (LL.ListLike (s el) el, MonadIO m)
+         => (el -> m ()) -> IterateeG s el m ()
+mapM_ f = IterateeG step
+  where
+  step (Chunk xs) | LL.null xs = return $ Cont (IterateeG step) Nothing
+  step (Chunk xs)              = do LL.mapM_ f xs
+                                    return $ Cont (IterateeG step) Nothing
+  step stream                  = return $ Done () stream
+
+{-# INLINE mapM_ #-}
diff --git a/src/Data/Iteratee/Char.hs b/src/Data/Iteratee/Char.hs
--- a/src/Data/Iteratee/Char.hs
+++ b/src/Data/Iteratee/Char.hs
@@ -31,7 +31,7 @@
 where
 
 import qualified Data.Iteratee.Base as Iter
-import Data.Iteratee.Base hiding (break)
+import Data.Iteratee.Base hiding (break, last)
 import Data.Char
 import Control.Monad.IO.Class
 import qualified Data.ListLike as LL
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
@@ -9,9 +9,11 @@
   -- * File enumerators
   -- ** FileDescriptor based enumerators
   enumFd
+  ,enumFdFollow
   ,enumFdRandom
   -- * Iteratee drivers
   ,fileDriverFd
+  ,fileDriverFollowFd
   ,fileDriverRandomFd
 #endif
 )
@@ -65,6 +67,34 @@
     check p  (Cont i Nothing) = loop i p
     check _p (Cont _ (Just e)) = return $ throwErr e
 
+-- |The enumerator of a POSIX File Descriptor: a variation of enumFd
+-- that follows the tail of growing input.
+enumFdFollow :: forall s el a.(ReadableChunk s el) =>
+  Fd ->
+  EnumeratorGM s el IO a
+enumFdFollow fd iter' =
+  liftIO (mallocForeignPtrBytes (fromIntegral buffer_size)) >>= loop iter'
+  where
+    buffer_size = fromIntegral $ 4096 - mod 4096 (sizeOf (undefined :: el))
+    loop iter fp = do
+      s <- readFollow iter fp
+      checkres fp iter s
+    readFollow iter fp = do
+        liftIO . withForeignPtr fp $ \p -> do
+          liftIO $ GHC.Conc.threadWaitRead fd
+          n <- myfdRead fd (castPtr p) buffer_size
+          case n of
+            Left _errno -> return $ Left "IO error"
+            Right 0 -> do liftIO $ threadDelay (250 * 1000)
+                          readFollow iter fp
+            Right n' -> liftM (Right . Just) $ readFromPtr p (fromIntegral n')
+    checkres fp iter = either (flip enumErr iter)
+                              (maybe (return iter)
+                                     (check fp <=< runIter iter . Chunk))
+    check _p (Done x _) = return . return $ x
+    check p  (Cont i Nothing) = loop i p
+    check _p (Cont _ (Just e)) = return $ throwErr e
+
 -- |The enumerator of a POSIX File Descriptor: a variation of enumFd that
 -- supports RandomIO (seek requests)
 enumFdRandom :: forall s el m a.(ReadableChunk s el, MonadIO m) =>
@@ -127,6 +157,23 @@
 fileDriverFd iter filepath = do
   fd <- liftIO $ openFd filepath ReadOnly Nothing defaultFileFlags
   result <- enumFd fd iter >>= run
+  liftIO $ closeFd fd
+  return result
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFdFollow as a convenience.
+-- The first iteratee is used to scan through to the end of the file, using
+-- enumFd. The second iteratee is used from then onwards on the growing tail
+-- of the file, using enumFdFollow.
+fileDriverFollowFd :: (ReadableChunk s el) =>
+  IterateeG s el IO a ->
+  (a -> IterateeG s el IO b) ->
+  FilePath ->
+  IO b
+fileDriverFollowFd scanIter followIter filepath = do
+  fd <- liftIO $ openFd filepath ReadOnly Nothing defaultFileFlags
+  state <- enumFd fd scanIter >>= run
+  result <- enumFdFollow fd (followIter state) >>= run
   liftIO $ closeFd fd
   return result
 
diff --git a/src/Data/Iteratee/IO/Interact.hs b/src/Data/Iteratee/IO/Interact.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Interact.hs
@@ -0,0 +1,31 @@
+module Data.Iteratee.IO.Interact (
+  ioIter
+) where
+
+import Control.Monad.IO.Class (MonadIO, liftIO)
+
+import Data.Iteratee
+import qualified Data.Iteratee.Base.StreamChunk as SC
+
+-- | Use an IO function to choose what iteratee to run.
+-- Typically this function handles user interaction and
+-- returns with a simple iteratee such as 'head' or 'seek'.
+-- 
+-- The IO function takes a value of type 'a' as input, and
+-- should return 'Right a' to continue, or 'Left b'
+-- to terminate. Upon termination, ioIter will return 'Done b'.
+--
+-- The second argument to 'ioIter' is used as the initial input
+-- to the IO function, and on each successive iteration the
+-- previously returned value is used as input. Put another way,
+-- the value of type 'a' is used like a fold accumulator.
+-- The value of type 'b' is typically some form of control code
+-- that the application uses to signal the reason for termination.
+ioIter :: (SC.StreamChunk s el, MonadIO m)
+       => (a -> IO (Either b (IterateeG s el m a))) -> a -> IterateeG s el m b
+ioIter f a = do i'e <- liftIO $ f a
+                case i'e of
+                     Left e  -> return e
+                     Right i -> do a' <- i
+                                   ioIter f a'
+
diff --git a/tests/QCUtils.hs b/tests/QCUtils.hs
--- a/tests/QCUtils.hs
+++ b/tests/QCUtils.hs
@@ -9,7 +9,7 @@
 import Data.Iteratee
 import qualified Data.Iteratee as I
 import Data.Iteratee.Base.StreamChunk (StreamChunk)
-import Control.Monad.Identity
+import Data.Functor.Identity
 
 -- Show instance
 instance (Show a, StreamChunk s el) => Show (IterateeG s el Identity a) where
diff --git a/tests/testIteratee.hs b/tests/testIteratee.hs
--- a/tests/testIteratee.hs
+++ b/tests/testIteratee.hs
@@ -14,7 +14,7 @@
 import qualified Data.Iteratee.Char as IC
 import qualified Data.Iteratee as Iter
 import qualified Data.Iteratee.Base.StreamChunk as SC
-import Control.Monad.Identity
+import Data.Functor.Identity
 import Data.Monoid
 import qualified Data.ListLike as LL
 
