Comment by microtonal

14 years ago

As elegant and appealing as Haskell's purely functional foundation is, it prohibits simple, but crucial, impure tasks such as writing to files and communicating over networks. [...] but they make it neither intuitive nor easy.

Actually, such tasks have become much simpler thanks to the recent surge of packages such as conduit. These make typical I/O work easy, and very much resemble a UNIX pipe. You have a pure or impure source that produces data (e.g. reading a file), conduits that manipulate data, and sinks that consume data (e.g. writing data to a file, or storing it in a list).

In fact, I have started to like conduit so much, that I/O in other languages feels quite kludgy.

If you wish to debug a function in Haskell you can't insert a printf to inspect its inner workings unless that function happens to be on the IO monad.

Actually, you can, using Debug.Trace.trace, which takes a String and an expression. The string is first printed, then the expression is evaluated. No need to be in the IO monad:

http://www.haskell.org/haskellwiki/Debugging#Printf_and_frie...

After a frustrutating hour with my Haskell version I was still struggling to understand the monadic API required to work with the XML parser and I gave up.

I had to parse some XML data and used hexpat-pickle. Once you get the hang of it, it is pretty simple. But I agree that there is a learning curve involved.

With such tasks, it usually takes longer to come up with an initial Haskell solution. But then that solution is usually very clean and elegant. If you have to write something quickly, it is not ideal, but often the things that we wrote quickly end up being used for years anyhow ;).

By the way, my experience is exactly the opposite. I wrote some code in Clojure (which I do like very much, since it manages to leverage the Java platform while staying very clean). But I noticed that my productivity dropped - lots of things that Haskell detected through static type checking I had to verify myself by hand and write tests for.

it usually takes longer to come up with an initial Haskell solution. But then that solution is usually very clean and elegant

The big thing that jumps out at me like that in my experience is: it takes longer to come up with an initial Haskell solution that compiles, but when it compiles, it almost always works first-time; while, in a less rigorous language, I can get it compiling a bit sooner, but any time saved there is lost many-fold when it doesn't actually work.

A bit off topic, but a question I've been wondering:

How do conduits differ from arrows? They seem to occupy a similar space. Is one more general than the other?

  • They actually occupy radically different spaces. Arrows are a generalized computational tool, along the lines of applicative functors and monads. Conduits are basically the next step from iteratees. Iteratees are essentially a functional/compositional way of dealing with I/O. Prior to iteratees, you essentially had to choose between using lazy I/O and ignoring the problems or writing C in Haskell if you needed performance and determinism. Conduits take the benefits of iteratees, simplify the coding and give you more concrete guarantees about when resources will be acquired and released.

    In fact conduits/pipes/iteratees are all more useful than arrows outside academia. The only library you're likely to encounter arrows in is HXT and there are other XML libraries. You're likely to encounter one of the conduit/pipe/iteratee libraries using any Haskell web framework and probably any other system doing a lot of I/O where guarantees need to be had.

    I haven't yet seen evidence that arrows are actually useful at all outside very limited circumstances. The difficulty of learning them and using them is exacerbated by the fact they have something like 11 laws you must obey to create your own arrow versus three for a monad. There's also a lot more going on in the pretty arrow syntax than in do-notation, which desugars pretty easily by comparison. It would all be justified, I suppose, if using arrows enabled crazy functionality that isn't available via simpler abstractions, but that just doesn't seem to be the case.