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'