Pipes define a free monad
The core type of the pipes
library is a free monad.
Indeed,
Pipes.Proxy
is equivalent to Free (ProxyF _ _ _ _ _)
, where
data Free f a
= Free (f (Free f a))
| Pure a
data ProxyF a' a b' b m x
= Request a' (a -> x)
| Respond b (b' -> x)
| M (m x)
That’s nothing ground-breaking if you’re familiar with free monads. In fact, it used to be the literal definition of pipes, using free before I even knew Haskell was a thing.
Nevertheless, my previous understanding of pipes was mostly based on the
interface (which is very nice by the way; the diagrams in
Pipes.Core
are a pleasure to contemplate), while I needed all my focus to follow the
implementation. Hence, in spite of now extensive experience with free monads,
it took one more wander in front of the source code of pipes for me to have
that epiphany.
A much deeper understanding of the whole business followed immediately. With free monads as a common starting point1, it became much easier to compare pipes, conduit, and quiver: basically, they’re all using free monads for differing I/O-like functors2.
Now it may be just another occasionally useful fun fact, but that one somehow felt like a personal achievement. That realization gave me a strong impression of the power of abstraction.