Revision as of 20:42, 4 March 2011

An enumerator is something that knows how to generate a list and an iteratee is something that does one step in processing another piece of the big list. E.g. to sum up all elements of a list, we do

foldl (+) 0 xs

Then foldl is the enumerator and ((+),0) is the iteratee.

Clearly the function that sums the current element with the accumulator, (+), doesn't know or care from which collection the elements are coming from. The initial seed, 0, is again unaware of the collection. That achieves the
separation of concerns: fold (aka, enumerator) has the intimate knowledge
of the collection and how to get to the next element; iteratee knows
what to do with the current element.

Contents

1 Definition

Do not rely on the foldl analogy too firmly, it is misleading. ((+),0) is an F-algebra and foldl (+) 0 is a catamorphism. But iteratee is different, it is automaton. From this point of view, enumerator sends elements of a list from head to tail sequentially as input messages to iteratee. If iteratee finishes, it outputs an accumulator. If iteratee continues, it outputs nothing (i.e. ()).

So, a set of states of iteratee is divided into subsets "Done" and "Next". Done-state means that automaton finished consuming a list, i.e. automaton is dead. Next-state means that you can give an input message and obtain the same automaton in a new state.

data Iteratee i o
= Done o
| Next (i -> Iteratee i o)

i is a type of input messages (or list elements) and o is a type of the output message (an accumulator). Precisely speaking, Iteratee stores not an automaton, but an automaton in some state, an automaton with distinguished state. As you see, if Iteratee is Next, then we have a function that takes an input message and returns a new Iteratee.

The distinct feature of iteratee is that it can say after which list element an iteratee finishes. An iteratee says this by sending "Done" to an enumerator. Then the enumerator can close a file or a socket (a stream) where a list of characters is read from. Lazy I/O, which uses lazy lists, close a stream only when the stream is exhausted.

The drawback is that enumerator can not tell iteratee that a list is exhausted. An Iteratee consumes only infinite lists. You can remedy this by assuming

i == Maybe i'

where i' is a type of list elements. Nothing given to iteratee signals that the list is exhausted.

2 Functions

You can compose iteratees sequentially in time. This is done by (>>). it0 >> it1 means that when it0 finishes, it1 starts. Generally speaking, Iteratee i is a Monad. And it works exactly as monadic parser.

You can compose iteratees sequentially in space. it0's output messages become it1's input messages. So it0 and it1 works in parallel. Their composition is denoted it1 . it0. If it0 finishes, it is resurrected to its original state. If it1 finishes, it1 . it0 finishes. The main feature here is that it0 is restarted. This is used for repetitive parsing.

3 Generalization

You may note that Iteratee is final coalgebra. Other kinds of automata can be described with other F-coalgebras. In practice such automata can handle network protocols or interactive user input. See for example papers by Bart Jacobs for theoretical discussion.