[Haskell-cafe] Fwd: Semantics of iteratees, enumerators, enumeratees?

John Millikin jmillikin at gmail.com
Wed Aug 25 11:43:18 EDT 2010

On Wed, Aug 25, 2010 at 01:33, John Lato <jwlato at gmail.com> wrote:
> Is this really true?  Consider iteratees that don't have a sensible default
> value (e.g. head) and an empty stream.  You could argue that they should
> really return a Maybe, but then they wouldn't be divergent in other
> formulations either.  Although I do find it interesting that EOF is no
> longer part of the stream at all.  That may open up some possibilities.

Divergent iteratees, using the current libraries, will simply throw an
exception like "enumEOF: divergent iteratee". There's no way to get
useful values out of them. Disallowing returning Continue when given
an EOF prevents this invalid state.

> Also, I found this confusing because you're using Result as a data
> constructor for the Step type, but also as a separate type constructor.  I
> expect this could lead to very confusing error messages ("What do you mean
> 'Result b a' doesn't have type 'Result'?")

Oh, sorry, those constructors should be something like this (the
system on which I wrote that email has no Haskell compiler, so I
couldn't verify types before sending):

   data Step a b = Continue (a -> Step a b) (Result a b)
                 | GotResult (Result a b)

The goal is to let the iteratee signal three states:

* Can accept more input, but terminating the stream now is acceptable
* Requires more input, and terminating the stream now is an error
* Cannot accept more input

> I find this unclear as well, because you've unpacked the continue parameter
> but not the eof.  I would prefer to see this as:
>     type Enumerator a b = (a -> Step a b) -> Result a b -> Step a b
> However, is it useful to do so?  That is, would there ever be a case where
> you would want to use branches from separate iteratees?  If not, then why
> bother unpacking instead of just using
>     type Enumerator a b = Step a b -> Step a

When an enumerator terminates, it needs to pass control to the next
enumerator (the final enumerator is enumEOF). Thus, the second "step"
parameter is actually the next enumerator to run in the chain (aka the
calling enumerator).

More information about the Haskell-Cafe mailing list