
Paolo Capriotti wrote:
On Mon, Apr 16, 2012 at 10:13 PM, Ben Franksen
wrote: (1) What is the reason for the asymmetry in
type Producer b m = Pipe () b m type Consumer a m = Pipe a Void m
i.e. why does Producer use () for the input? I would expect it to use Void, like Consumer does for its output. Calling await in a Producer resulting in an immediate 'return ()' as you say is allowed (in the tutorial) strikes me as not very useful.
The underlying reason for the asymmetry is the fact that '()' is a terminal object in the category of haskell types and *total* functions, while 'Void' is an initial object.
Here's a property that uniquely determines the definitions of 'Producer' above. Let 'X' be the type such that 'Producer b m = Pipe X b m'. For all producers 'p' there should be a unique (total) pipe 'alpha :: forall a r. Pipe a X m r' such that 'alpha >+> p' and 'p' are observationally equal. In other words, since a producer "never uses values of its input type 'a'", there should be a unique way to make it into a pipe which is polymorphic in 'a'. It's easy to see that this property immediately implies that 'X' should be a terminal object, i.e. '()', and 'alpha' is therefore 'pipe (const ())'.
Dually, you obtain that 'Consumer a m' is necessarily 'Pipe a Void m', and 'alpha = pipe absurd'.
Ok, thanks for the explanation. Makes sense...
(2) The $$ operator is poorly named. I would intuitively expect an operator that looks so similar to the standard $ to have the same direction of data flow (i.e. right to left, like function application and composition) but your is left to right. You could use e.g. >$> instead, which has the additional advantage of allowing a symmetric variant for the other direction i.e. <$<.
'$$' is inspired by iteratees. Similarly to its iteratee counterpart, it discards upstream result values and only returns the output of the last pipe. That said, '>$>' looks like a clearer alternative, so I could consider changing it.
(...or maybe use a plain function instead of an operator...) Cheers Ben