Types

The underlying datatype for all the types in this package. In has four
type parameters:

i is the type of values for this Pipe's input stream.

o is the type of values for this Pipe's output stream.

m is the underlying monad.

r is the result type.

Note that o and r are inherently different. o is the type of the
stream of values this Pipe will produce and send downstream. r is the
final output of this Pipe.

Pipes can be composed via the pipe function. To do so, the output type
of the left pipe much match the input type of the left pipe, and the result
type of the left pipe must be unit (). This is due to the fact that any
result produced by the left pipe must be discarded in favor of the result of
the right pipe.

Request more input from upstream. The first field takes a new input
value and provides a new Pipe. The second is for early termination. It
gives a new Pipe which takes no input from upstream. This allows a
Pipe to provide a final stream of output values after no more input is
available from upstream.

Require running of a monadic action to get the next Pipe. Second
field is an early cleanup function. Technically, this second field
could be skipped, but doing so would require extra operations to be
performed in some cases. For example, for a Pipe pulling data from a
file, it may be forced to pull an extra, unneeded chunk before closing
the Handle.

A Pipe which provides a stream of output values, without consuming any
input. The input parameter is set to Void to indicate that this Pipe
takes no input. A Source is not used to produce a final result, and thus
the result parameter is set to ().

A Pipe which consumes a stream of input values and produces a final
result. It cannot produce any output values, and thus the output parameter
is set to Void. In other words, it is impossible to create a HaveOutput
constructor for a Sink.

Previously, we just had a plain action. However, most Pipes simply have
empty cleanup actions, and storing a large set of them wastes memory. But
having strict fields and distinguishing between pure and impure actions, we
can keep memory usage constant, and only allocate memory for the actual
actions we have to track.

Same as pipe, but retain both the new left pipe and the leftovers from
the right pipe. The two components are combined together into a single pipe
and returned, together with the result of the right pipe.

Note: we're biased towards checking the right side first to avoid pulling
extra data which is not needed. Doing so could cause data loss.

A Sink has a Void type parameter for the output, which makes it
difficult to compose with Sources and Conduits. This function replaces
that parameter with a free variable. This function is essentially id; it
only modifies the types, not the actions performed.