packages feed

enumerator-0.4.6: src/types.anansi

\section{Core types}

Most of this library's types and functions are exported from the
{\tt Data.Enumerator} module.

:f Data/Enumerator.hs
|Data.Enumerator module header|
module Data.Enumerator (
	|Data.Enumerator exports|
	) where
|Data.Enumerator imports|
:

\noindent A few utility functions share names with functions from the Prelude, so
those are removed from the default namespace.

:d Data.Enumerator imports
import qualified Prelude as Prelude
import Prelude hiding (
	|excluded Prelude imports|
	)
:

:d Data.Enumerator exports
-- * Core
-- ** Types
  Stream (..)
, Iteratee (..)
, Step (..)
, Enumerator
, Enumeratee
:

\subsection{Input streams}

A {\tt Stream} is a sequence of chunks generated by an enumerator or
enumeratee. Chunks might be composite values, such as a string, or atomic,
such as a parser event. Allowing a stream to support multiple chunks
slightly complicates iteratee and enumeratee implementation, but greatly
simplifies handling of leftover inputs.

{\tt (Chunks [])} is a legal value, used when a stream is still active but
no data is currently available. Iteratees and enumeratees often special-case
empty chunks for performance reasons, though they're not required to.

:f Data/Enumerator.hs
|apidoc Data.Enumerator.Stream|
data Stream a
	= Chunks [a]
	| EOF
	deriving (Show, Eq)

instance Monad Stream where
	return = Chunks . return
	Chunks xs >>= f = mconcat (fmap f xs)
	EOF >>= _ = EOF

instance Functor Stream where
	fmap f (Chunks xs) = Chunks (fmap f xs)
	fmap _ EOF = EOF

-- | Since: 0.4.5
instance A.Applicative Stream where
	pure = return
	(<*>) = CM.ap
:

The {\tt Monoid} instance deserves some special attention, because it has
the unexpected behavior that {\tt mappend EOF (Chunks []) == EOF}. Although
it's reasonable that appending chunks to an {\sc eof} stream should provide
a valid stream, such behavior would violate the monoid laws.

:d Data.Enumerator imports
import Data.Monoid (Monoid, mempty, mappend, mconcat)
:

:f Data/Enumerator.hs
instance Monoid (Stream a) where
	mempty = Chunks mempty
	mappend (Chunks xs) (Chunks ys) = Chunks (xs ++ ys)
	mappend _ _ = EOF
:

\subsection{Iteratees}

The primary data type for this library is {\tt Iteratee}, which consumes
input until it either generates a value or encounters an error. Rather
than requiring all input at once, an iteratee will return {\tt Continue}
when it is capable of processing more data.

In general, iteratees begin in the {\tt Continue} state. As each chunk is
passed to the continuation, the iteratee may return the next step, which is
one of:

\begin{itemize}
\item {\tt Continue}: The iteratee is capable of accepting more input. Note
that more input is not required; the iteratee might be able to generate a
value immediately if the stream ends.

\item {\tt Yield}: The iteratee has received enough input to generate a
result. Included in this value is left-over input, which can be passed to
the next iteratee.

\item {\tt Error}: The iteratee encountered an error which prevents it from
proceeding further.
\end{itemize}

:d Data.Enumerator imports
import qualified Control.Exception as Exc
:

:f Data/Enumerator.hs
data Step a m b
	|apidoc Data.Enumerator.Continue|
	= Continue (Stream a -> Iteratee a m b)
	
	|apidoc Data.Enumerator.Yield|
	| Yield b (Stream a)
	
	|apidoc Data.Enumerator.Error|
	| Error Exc.SomeException

|apidoc Data.Enumerator.Iteratee|
newtype Iteratee a m b = Iteratee
	{ runIteratee :: m (Step a m b)
	}
:

Users often need to construct iteratees which only yield or continue,
so we define some helper functions to save typing:

:f Data/Enumerator.hs
|apidoc Data.Enumerator.returnI|
returnI :: Monad m => Step a m b -> Iteratee a m b
returnI step = Iteratee (return step)

|apidoc Data.Enumerator.yield|
yield :: Monad m => b -> Stream a -> Iteratee a m b
yield x extra = returnI (Yield x extra)

|apidoc Data.Enumerator.continue|
continue :: Monad m => (Stream a -> Iteratee a m b)
         -> Iteratee a m b
continue k = returnI (Continue k)
:

:d Data.Enumerator exports
, returnI
, yield
, continue
:

\subsection{Enumerators}

Enumerators typically read from an external source (parser, handle, random
number generator, etc). They feed chunks into an iteratee until the source
runs out of data (triggering {\tt EOF}) or the iteratee finishes processing
(yields a value).

Since {\tt Iteratee} is an alias for {\tt m (Step a m b)}, enumerators can
also be considered step transformers of type
{\tt Step a m b -> m (Step a m b)}.

:f Data/Enumerator.hs
|apidoc Data.Enumerator.Enumerator|
type Enumerator a m b = Step a m b -> Iteratee a m b
:

Although enumerators can be encoded as a simple step transformer with the
type {\tt Step a m b -> Step a m b}, encoding as a computation allows easier
reasoning about the order of side effects. Consider the case of enumerating
two files:

:d enumerator example
let iterFoo = enumFile "foo.txt" iterWhatever
let iterBar = enumFile "bar.txt" iterFoo
:

It's impossible to determine, merely by looking at these lines, which file
will be opened first. In fact, depending on the implementation of
{\tt enumFile}, both files might be open at the same time. If enumerators
return monadic values, the order of events is more clear:

:d enumerator example
iterFoo <- enumFile "foo.txt" iterWhatever
iterBar <- enumFile "bar.txt" iterFoo
:

\subsection{Enumeratees}

In cases where an enumerator acts as both a source and sink, the resulting
type is named an {\tt Enumeratee}. Enumeratees have two input types,
``outer a'' ({\tt ao}) and ``inner a'' ({\tt ai}).

Enumeratees are encoded as an iteratee stack. The outer iteratee reads from
a stream of \emph{ao} values, transforms them into \emph{ai}, and passes them
to an inner iteratee. This model allows a single outer input to generate many
inner inputs, and vice-versa.

:f Data/Enumerator.hs
|apidoc Data.Enumerator.Enumeratee|
type Enumeratee ao ai m b = Step ai m b
          -> Iteratee ao m (Step ai m b)
:

\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.

For compatibility, {\tt (==<<)} is aliased to {\tt (\$\$)}.

:f Data/Enumerator.hs
infixl 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)
:

:f Data/Enumerator.hs
infixr 1 ==<<

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

:f Data/Enumerator.hs
infixr 0 $$

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

:f Data/Enumerator.hs
infixr 1 >==>

|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
:

:f Data/Enumerator.hs
infixr 1 <==<

|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 (>==>)
:

:d Data.Enumerator exports
-- ** Operators
, (>>==)
, (==<<)
, ($$)
, (>==>)
, (<==<)
:

\subsection{Iteratees as Monads}

Iteratees are monads; by sequencing iteratees, very complex processing may
be applied to arbitrary input streams. Iteratees are also applicative
functors and monad transformers.

:f Data/Enumerator.hs
instance Monad m => Monad (Iteratee a m) where
	return x = yield x (Chunks [])
	
	m >>= f = Iteratee $ runIteratee m >>=
		\r1 -> case r1 of
			Continue k -> return (Continue ((>>= f) . k))
			Error err -> return (Error err)
			Yield x (Chunks []) -> runIteratee (f x)
			Yield x extra -> runIteratee (f x) >>=
				\r2 -> case r2 of
					Continue k -> runIteratee (k extra)
					Error err -> return (Error err)
					Yield x' _ -> return (Yield x' extra)
:

Most iteratees are used to wrap \io{} operations, so it's sensible to define
instances for typeclasses from {\tt transformers}.

:d Data.Enumerator imports
import Control.Monad.Trans.Class (MonadTrans, lift)
import Control.Monad.IO.Class (MonadIO, liftIO)
:

:f Data/Enumerator.hs
instance MonadTrans (Iteratee a) where
	lift m = Iteratee (m >>= runIteratee . return)

instance MonadIO m => MonadIO (Iteratee a m) where
	liftIO = lift . liftIO
:

It's probably possible to define {\tt Functor} and {\tt Applicative}
instances for {\tt Iteratee} without a {\tt Monad} constraint, but I haven't
bothered, since every useful operation requires {\tt m} to be a Monad anyway.

:d Data.Enumerator imports
import qualified Control.Applicative as A
import qualified Control.Monad as CM
:

:f Data/Enumerator.hs
instance Monad m => Functor (Iteratee a m) where
	fmap = CM.liftM
:

:f Data/Enumerator.hs
instance Monad m => A.Applicative (Iteratee a m) where
	pure = return
	(<*>) = CM.ap
:

:d Data.Enumerator imports
import Data.Typeable ( Typeable, typeOf
                     , Typeable1, typeOf1
                     , mkTyConApp, mkTyCon)
:

:f Data/Enumerator.hs
-- | Since: 0.4.6
instance (Typeable a, Typeable1 m) => Typeable1 (Iteratee a m) where
	typeOf1 i = mkTyConApp tyCon [typeOf a, typeOf1 m] where
		tyCon = mkTyCon "Data.Enumerator.Iteratee"
		(a, m) = peel i
		
		peel :: Iteratee a m b -> (a, m ())
		peel = undefined
: