diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.7.0.0
+version:       0.7.0.1
 synopsis:      Iteratee-based I/O
 description:
   The Iteratee monad provides strict, safe, and functional I/O. In addition
@@ -73,6 +73,7 @@
     Data.Iteratee.Exception
     Data.Iteratee.IO
     Data.Iteratee.IO.Handle
+    Data.Iteratee.IO.Interact
     Data.Iteratee.Iteratee
     Data.Iteratee.ListLike
 
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
@@ -139,13 +139,22 @@
   {-# INLINE return #-}
   return x = Iteratee $ \onDone _ -> onDone x (Chunk empty)
   {-# INLINE (>>=) #-}
-  m >>= f = Iteratee $ \onDone onCont ->
-     let m_done a (Chunk s)
-           | nullC s      = runIter (f a) onDone onCont
-         m_done a stream = runIter (f a) (const . flip onDone stream) f_cont
-           where f_cont k Nothing = runIter (k stream) onDone onCont
-                 f_cont k e       = onCont k e
-     in runIter m m_done (onCont . ((>>= f) .))
+  (>>=) = bindIteratee
+
+{-# INLINE bindIteratee #-}
+bindIteratee :: (Monad m, Nullable s)
+    => Iteratee s m a
+    -> (a -> Iteratee s m b)
+    -> Iteratee s m b
+bindIteratee = self
+    where
+        self m f = Iteratee $ \onDone onCont ->
+             let m_done a (Chunk s)
+                   | nullC s      = runIter (f a) onDone onCont
+                 m_done a stream = runIter (f a) (const . flip onDone stream) f_cont
+                   where f_cont k Nothing = runIter (k stream) onDone onCont
+                         f_cont k e       = onCont k e
+             in runIter m m_done (onCont . (flip self f .))
 
 instance NullPoint s => MonadTrans (Iteratee s) where
   lift m = Iteratee $ \onDone _ -> m >>= flip onDone (Chunk empty)
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,28 @@
+module Data.Iteratee.IO.Interact (
+  ioIter
+)
+where
+
+import Control.Monad.IO.Class
+import Data.Iteratee
+
+-- | 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 :: (MonadIO m, Nullable s)
+       => (a -> IO (Either b (Iteratee s m a)))
+       -> a
+       -> Iteratee s m b
+ioIter f a = either return (>>= ioIter f) =<< liftIO (f a)
+{-# INLINE ioIter #-}
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
@@ -95,11 +95,13 @@
 identity = idone () (Chunk empty)
 
 -- |Get the stream status of an iteratee.
-isStreamFinished :: Monad m => Iteratee s m (Maybe SomeException)
+isStreamFinished :: (Monad m, Nullable s) => Iteratee s m (Maybe SomeException)
 isStreamFinished = liftI check
   where
+    check s@(Chunk xs)
+      | nullC xs  = isStreamFinished
+      | otherwise = idone Nothing s
     check s@(EOF e) = idone (Just $ fromMaybe (toException EofException) e) s
-    check s         = idone Nothing s
 {-# INLINE isStreamFinished #-}
 
 
@@ -162,10 +164,12 @@
   (acc -> Iteratee s m (acc, s'))
   -> acc
   -> Enumeratee s s' m a
-unfoldConvStream f acc = eneeCheckIfDone check
+unfoldConvStream f acc0 = eneeCheckIfDone (check acc0)
   where
-    check k = isStreamFinished >>= maybe (step k) (idone (liftI k) . EOF . Just)
-    step k = f acc >>= \(acc', s') -> unfoldConvStream f acc' . k . Chunk $ s'
+    check acc k = isStreamFinished >>=
+                    maybe (step acc k) (idone (liftI k) . EOF . Just)
+    step acc k = f acc >>= \(acc', s') ->
+                    eneeCheckIfDone (check acc') . k . Chunk $ s'
 
 
 joinI ::
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
@@ -18,6 +18,7 @@
   ,dropWhile
   ,drop
   ,head
+  ,last
   ,heads
   ,peek
   ,roll
@@ -46,7 +47,7 @@
 )
 where
 
-import Prelude hiding (null, head, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product)
+import Prelude hiding (null, head, last, drop, dropWhile, take, break, foldl, foldl1, length, filter, sum, product)
 
 import qualified Data.ListLike as LL
 import qualified Data.ListLike.FoldableLL as FLL
@@ -133,6 +134,19 @@
     | True         = idone (LL.head vec) (Chunk $ LL.tail vec)
   step stream      = icont step (Just (setEOF stream))
 {-# INLINE head #-}
+
+-- |Attempt to read the last element of the stream and return it
+-- Raise a (recoverable) error if the stream is terminated
+--
+-- The analogue of @List.last@
+last :: (Monad m, LL.ListLike s el, Nullable s) => Iteratee s m el
+last = liftI (step Nothing)
+  where
+  step l (Chunk xs)
+    | nullC xs     = liftI (step l)
+    | otherwise    = liftI $ step (Just $ LL.last xs)
+  step l s@(EOF _) = maybe (icont (step l) . Just . setEOF $ s) return l
+{-# INLINE last #-}
 
 
 -- |Given a sequence of characters, attempt to match them against
