An enumeratee acts as a stream adapter; place one between an enumerator
and an iteratee, and it changes the type or contents of the input stream.

Most users will want to combine enumerators, enumeratees, and iteratees
using the stream combinators joinI and joinE, or their operator aliases
(=$) and ($=). These combinators are used to manage how left-over input
is passed between elements of the data processing pipeline.

WARNING: due to the current encoding of iteratees in this library,
careless use of the yield primitive may violate the monad laws.
To prevent this, always make sure that an iteratee never yields
extra data unless it has received at least one input element.

More strictly, iteratees may not yield data that they did not
receive as input. Don't use yield to “inject” elements
into the stream.

A common pattern in Enumerator implementations is to check whether
the inner Iteratee has finished, and if so, to return its output.
checkContinue0 passes its parameter a continuation if the Iteratee
can still consume input; if not, it returns the iteratee's step.

The type signature here is a bit crazy, but it's actually very easy to
use. Take this code:

A common pattern in Enumeratee implementations is to check whether
the inner Iteratee has finished, and if so, to return its output.
checkDone passes its parameter a continuation if the Iteratee
can still consume input, or yields otherwise.