<div dir="ltr"><div><b>Programming style</b></div><div><br></div>Conduits and pipes use a categorical programming model in continuation-passing style, and implement Monad instances that lift over an arbitrary base monad. The io-streams library is not polymorphic over the base monad, instead preferring to fix computations to IO. It's worth pointing out that stream transformation in io-streams style is also (implicitly) categorical in style, but in the Kleisli category for IO.<div><br></div><div>Conduits and pipes take responsibility for controlling the evaluation of the streaming computation in its entirety, while you can either treat an Input/OutputStream from io-streams like a Handle and feed work to it element-wise, or chain an Input and OutputStream together using a combinator like "connect" in streaming style.<br></div><div><br></div><div>IO-streams only does one-way streaming, while pipes and conduits can pass data back and forth between different stream transducers.</div><div><br></div><div><b>Resources and exceptions</b></div><div><br></div><div>The io-streams library purposely does not do any resource management for you, with the exception of a few "with*" functions that do some bracketing for you. The fact that all io-streams computations run in the base IO monad makes exception handling cheap there, but with the disadvantage that you e.g. cannot write a stream transducer that will install an exception handler, yield some elements, and then expect to be notified if a downstream consumer throws an exception. In general, in io-streams, if you're an InputStream or OutputStream that has yielded or consumed its value, you are not guaranteed that your continuation will ever be called again, so there is no opportunity for you to take on a cleanup action or release some resources -- that work has to be done outside the streaming computation. You can think of io-streams as little transducers that attach to resources.</div><div><br></div><div>Since pipes and conduits use continuation-passing monads, the exception handler problem arises there also, with the difference that since the pipes or conduits library is running the computation, it's possible to write a MonadCatch instance (or whatever) can carefully mask/unmask exceptions and thread an exception handler through the monad continuation for you. The issue with this is that every monad bind means a masking/unmasking of exceptions and the installation/cleanup of an exception handler. Usually people solve this by running in a ResourceT, which punts on the exception issue for resource handling by installing one exception handler and scoping all resource acquisition to explicit stack-like (Oleg's paper calls these "monadic regions") contexts.</div><div><br></div><div><b>Simplicity</b></div><div><br></div><div>Both pipes and conduits are <b>much</b> more complicated (especially re: type parameters), but you get additional features to go along with this complexity so there is an engineering tradeoff there. I designed io-streams to be as simple as possible and easy to understand for new Haskellers as possible, while giving me of the features I needed to implement our web programming framework.</div><div><br></div><div><b>Correctness/Laws</b></div><div><br></div><div>All three libraries do well on this front; pipes has had quite a bit of formal verification done to it, and purports to follow categorical laws. IO-streams is very simple and has 100% test coverage. Conduits is used by quite a few people so you can expect solidity there also (but I've never used it in anger).</div><div><br></div><div><b>Performance</b></div><div><b><br></b></div><div>All else being equal, an io-streams computation is likely to be slightly faster than the others because you pay a performance penalty at bind time for monadic polymorphism. Both pipes and conduits have leveraged their algebraic/categorical design, however, to create RULES pragmas to do some kinds of whole-computation stream fusion (i.e. map f . map g should get turned into "map (f . g)"), which might lessen this disadvantage or even eliminate it, depending on what kind of computation is happening.</div><div><br></div><div>Hope this helps</div><div>G</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Nov 5, 2015 at 8:33 AM, Daisuke Fujimura <span dir="ltr"><<a href="mailto:me@fujimuradaisuke.com" target="_blank">me@fujimuradaisuke.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hello cafe,<div><br></div><div>Now I'm preparing a presentatio for meetup for stream processing in functional programming(in Tokyo), and my part is something like "why stream processing library".</div><div><br></div><div>While preparing material, I encountered the question that why io-streams is so simple comparing others, and the reason and downsides behind its simplicity. I'm guessing it's due to its resource handling strategy, but not so confident about it because I couldn't came up with a concrete example yet.</div><div><br></div><div>Do you know any good example where io-streams won't fit? Let me know if you have.</div><div><br></div><div>Thanks!</div><div><br></div></div>
<br>_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
<br></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature">Gregory Collins <<a href="mailto:greg@gregorycollins.net" target="_blank">greg@gregorycollins.net</a>></div>
</div>