packages feed

enumerator-0.4.8: src/primitives.anansi

\section{Primitives}

\subsection{Operators}

Because {\tt Iteratee a m b} is semantically equivalent to
{\tt m (Step a m b)}, several of the monadic combinators ({\tt (>>=)},
{\tt (>=>)}, etc) are useful to save typing when constructing enumerators
and enumeratees. {\tt (>>==)} corresponds to {\tt (>>=)}, {\tt (>==>)} to
{\tt (>=>)}, and so on.

:d iteratee operators
infixl 1 >>==
infixr 1 ==<<
infixr 0 $$
infixr 1 >==>
infixr 1 <==<

|apidoc Data.Enumerator.(>>==)|
(>>==) :: Monad m
       => Iteratee a m b
       -> (Step a m b -> Iteratee a' m b')
       -> Iteratee a' m b'
i >>== f = Iteratee (runIteratee i >>= runIteratee . f)

|apidoc Data.Enumerator.(==<<)|
(==<<) :: Monad m
       => (Step a m b -> Iteratee a' m b')
       -> Iteratee a m b
       -> Iteratee a' m b'
(==<<) = flip (>>==)

|apidoc Data.Enumerator.($$)|
($$) :: Monad m
     => (Step a m b -> Iteratee a' m b')
     -> Iteratee a m b
     -> Iteratee a' m b'
($$) = (==<<)

|apidoc Data.Enumerator.(>==>)|
(>==>) :: Monad m
       => Enumerator a m b
       -> (Step a m b -> Iteratee a' m b')
       -> Step a m b
       -> Iteratee a' m b'
(>==>) e1 e2 s = e1 s >>== e2

|apidoc Data.Enumerator.(<==<)|
(<==<) :: Monad m
       => (Step a m b -> Iteratee a' m b')
       -> Enumerator a m b
       -> Step a m b
       -> Iteratee a' m b'
(<==<) = flip (>==>)
:

\subsection{Running iteratees}

To simplify running iteratees, {\tt run} sends {\tt EOF} and then examines
the result. It is not possible for the result to be {\tt Continue}, because
{\tt enumEOF} calls {\tt error} for divergent iteratees.

:d primitives
|apidoc Data.Enumerator.run|
run :: Monad m => Iteratee a m b
    -> m (Either Exc.SomeException b)
run i = do
	mStep <- runIteratee $ enumEOF ==<< i
	case mStep of
		Error err -> return $ Left err
		Yield x _ -> return $ Right x
		Continue _ -> error "run: divergent iteratee"
:

{\tt run\_} is even more simplified; it's used in simple scripts, where the
user doesn't care about error handling.

:d primitives
|apidoc Data.Enumerator.run_|
run_ :: Monad m => Iteratee a m b -> m b
run_ i = run i >>= either Exc.throw return
:

\subsection{Error handling}

Most real-world applications have to deal with error conditions; however,
libraries have various ways of reporting errors. Some throw exceptions,
others use callbacks, and many just use {\tt Either}. Heterogeneous error
handling makes composing code very difficult; therefore, all
enumerator-based code simply uses the standard {\tt Control.Exception}
module and its types.

Instances for the {\tt MonadError} class are provided in auxiliary
libraries, to avoid extraneous dependencies.

:d primitives
|apidoc Data.Enumerator.throwError|
throwError :: (Monad m, Exc.Exception e) => e -> Iteratee a m b
throwError exc = returnI (Error (Exc.toException exc))
:

Handling errors has a caveat: any input consumed before the error was
thrown can't be recovered. If an iteratee needs to continue parsing after an
error, either buffer the input stream or use a separate framing mechanism.

This limitation means that {\tt catchError} is mostly only useful for
transforming or logging errors, not ignoring them.

:d primitives
|apidoc Data.Enumerator.catchError|
catchError :: Monad m
           => Iteratee a m b
           -> (Exc.SomeException -> Iteratee a m b)
           -> Iteratee a m b
catchError iter h = iter >>== step where
	step (Yield b as) = yield b as
	step (Error err) = h err
	step (Continue k) = continue (\s -> k s >>== step)
: