I've spent some time learning about pipes and trying to improve the library so that it could be used in serious code, so I'll try to answer your questions as best as I can. +Gabriel Gonzalez
will certainly be able to provide further insight.
First of all, pipes don't have any exception or error handling built in. It is intended that you put all error handling in the base monad. So there is really no way for pipes to be exception-safe with regards to resource deallocation.
However, I don't see this as a limitation; rather, it is about separation of concerns. If you use, for example, ResourceT IO as your base monad, you can define perfectly safe pipes that read data from a file and close it as soon as the pipe terminates, or when an exception occurs. You should be able to use monadic regions for the same purpose, as well, although I haven't yet had a chance to try it.
Of course you can write a Pipe that holds an open handle until GC, but I'm pretty sure you can do that with enumerators and conduits as well. We just need to provide good basic pipes to deal with files and handles (http://github.com/pcapriotti/pipes-extra
is an initial stab at that), and everything will be doable by combining them through composition and other combinators.
Which brings me to what I think is the most important aspect of pipes: composability. Iteratees and enumerators don't really compose well as far as I know, and conduits are much better but still somewhat clunky, in my opinion. Eliminating the distinction between sources, sinks and transformers makes composition more powerful and easier to reason about. A big gripe of mine with conduit is the fact that they have several types of compositions that all seem to have the same semantics, but might actually have different behaviors, and you probably need to know the internals to understand exactly. Pipes have one
associative composition, and all pipes are monads.
About producers needing to be completely drained: that's actually not true. There is a strict composition defined in the pipes package, but I see it more as a curiosity than a useful operation, and I personally wouldn't have exposed it. It is not actually useful in practice and it doesn't guarantee resource finalization. The use case that you mention is implementable in pipes without any problem using lazy composition. Actually, you can convert any sink, source or conduit into a pipe in a way that preserves their termination behavior (see Control.Pipe.Conduit in pipes-extra).
I agree with +Michael Snoyman
that built-in chunking should not be necessary. You can work with chunked data if you want, and in that case there are ways to structure pipes to pass unconsumed data along. The idea of having an API that abstracts chunking away is interesting, and I suspect it could be built on top of Pipes. Again, I think it's important to keep various abstractions separate and composable, so that each one can be reasoned about independently.
As for performance, not much time has been put into micro-optimizations, but I did run a couple of benchmarks (stolen from conduit, you can find them in pipes-extra), and pipes are in the same ballpark as the other libraries. Pure pipes (i.e. those that don't have monadic effects) seem to be a lot faster than their conduit counterparts, though I'm not sure why.
I agree that there is still some work to be done to make pipes usable in practice, but I think the basic structure of the library is sound, and as soon as the guarded pipes (http://pcapriotti.wordpress.com/2012/02/02/an-introduction-to-guarded-pipes/
) concept is finalized, and initial libraries of combinators and IO pipes are released, they will be ready to be applied to real situations.