Saturday, October 20, 2007

How I'm Liking Haskell So Far

I've been playing with Haskell in my spare time for the past few months. So far, I have not found any major annoyances as I did with other languages I've been looking at recently (notably: Lua, Smalltalk, Erlang). Haskell's a keeper.

My major work in Haskell so far is a program to compute the 51 000 000 000 th letter in the string of words for the numbers 1 - 999 999 999 sorted lexicographically (problem description here), which I found through programming.reddit.com a while back. I was thoroughly impressed by Haskell's ability to symbolically represent the number words exactly. I defined a set of types that worked similarly to a grammar in specifying which words could go where. Ultimately I went with a slightly different approach that appeals better to my dynamically typed past, but I am hoping I will leave such habits behind after learning Haskell better.

While working to speed up the word numbers problem, I was pleasantly surprised by the simplicity of the profiler built into the Glasgow Haskell Compiler (GHC). You compile your program with the appropriate couple command line flags, then when you run the program, just pass it a couple more flags. The output is a nicely formatted text file showing where your cpu and memory allocation time went. I told GHC to just profile all top-level functions in my short program; if I had wanted, I could have designated specific functions or even expressions for profiling. I found the interface easier to use than GCC's profiling and the output easier to understand than Ruby's profiler.

Haskell has very beautiful syntax. Certainly nicer than OCaml and Erlang, and perhaps tied with Smalltalk as most-beautiful-syntax-I-know. It is clutter free; for example, using \args -> expr for the lambda syntax is simply brilliant. The language has a very 'mathy' feel to it that rubs me the right way. One thing I'm not as keen on is the idea that 'good software engineers' always document the types of their top-level functions. After using Ruby (and thus dynamic typing) for several years, I prefer to let the type inferencer do all that for me. It will be interesting to see how this opinion evolves over time.

To help me get a handle on monads and other weirdness, I picked up The Haskell School of Expression by Paul Hudak. It has been excellent, clearing up holes in my understanding from Yet Another Haskell Tutorial. I'm not so interested in the multimedia programming that the book uses as a motivation for introducing new language features, but it is done well.

I'm still not entirely comfortable with the way functional languages are taught. In the past I have learned a language by building a mental model of how the compiler might turn code constructs into machine language. This has served me well with C, C++, Java and even Ruby. With Haskell I am still trying to do this, but it gets confusing quickly. It doesn't help that functional languages' computational models are generally based on the lambda calculus rather than Von Neumann imperative stuff. This leads to descriptions of the language's semantics based on value substitution. While this may be an effective reasoning and proving tool, I just don't find it as satisfying as what's ultimately happening at the hardware level. I may have to read some papers on compiling functional languages before I'm happy :/

Haskell has three features which are totally new and weird to me: lazy evaluation, functional purity and monads. Lazy execution means that an expression will not be executed until its value is needed. Laziness seems really cool. You can define an infinite list of values and only use as many elements as you need without causing an infinite loop. Like closures, it seems to be one of those features that sounds weird and not very useful until you've thought about them for a while. Functional purity means that Haskell cannot do I/O in the usual way since a print statement is both side-effectful and doesn't have a sensible return value. The solution is to use these things called Monads.

Monads apparently are the hardest thing to learn for those new to Haskell, and this has been the case for me. I basically understand the mechanics of Monads thanks to The Haskell School of Expression and the wonderful blog post A Monad Tutorial for Ocaml. I still don't feel like I've got my head wrapped around them though. What's really going on with I/O under the monadal covers is still very unclear. But the idea that you can wrap stateful, imperative things up in this monad mechanism and end up with a purely functional result is terribly compelling even though it feels like magic.

Haskell is being really fun and I've learned a lot with more to come. If I stay it will be for the expressive semantics and beautiful syntax. If I leave for other languages, it will be for a more pragmatic (expedient, if dirty) approach, maybe O'Caml (though it has plenty flaws of its own).