At OSCON last week, my favorite tutorial was Simon Peyton-Jones’s A Taste of Haskell (A Taste of Haskell presentation materials (PDF)). I’ve dabbled with the language before (and pair programmed with Audrey Tang once to find and fix a bug in Pugs), but I’ve never really felt comfortable with the language.
It’s nice to report that function composition actually makes sense to me now. (At Foo Camp, I spent ten minutes explaining to Brian O’Sullivan why and how it tripped me up every other time I tried to figure it out, so hopefully Real World Haskell will smooth out that path for other people.
Most of my work these days is in C, though. As a junior language designer, I spend a lot of time considering the differences between languages (and, yes, rarely at the syntactic level).
I sat next to Nat Torkington at the tutorial. He kept rubbing his temples. At one point I leaned over and said, “The interesting thing about Haskell is that its functions only take one argument.” He turned green.
In all seriousness, well-factored Haskell code resembles well-factored Smalltalk code: if you have functions (or methods) longer than a handful of lines, you’re probably doing too much. Lower level languages such as C rarely give the opportunity for composition and abstraction that you can get out of functional languages. The presence of pure functions–functions which never change global state and which return the same output for the same input–is also immensely important.
It’s actually the combination of the two features which give these languages such power. When Haskell forces you to mark impure functions explicitly, it gives you tools to isolate behavior which can change global state in the smallest possible scopes, and prevents you from composing impure and pure code together accidentally. When Haskell lets you compose functions into larger functions, not only does it help you write code more concisely, but it provides well-defined units of behavior which work along well-defined and isolated boundaries.
If you can get your program into a succinct form where the important features of your algorithms are in isolated units, you can use tools such as Quick Check to test their behavior in isolation.
The desire to test small, individual pieces of a large pile of C code has gone further to convince me of the benefits of Haskell and similar language than anything else. I’m tired of keeping track of side effects and global state and trying to figure out exactly what I have to mock up before I have to tackle the work of linking and exporting symbols and calling a C function from a shared library.
I just don’t know how to write a concurrent-safe garbage collector in Haskell to use in a large, cross-platform project written otherwise mostly in C.