Monday, February 7, 2011

My Dinner with Haskell

It’s impossible to go very far into either the F# world or the world of domain-specific languages (DSLs) without encountering Haskell. It seems like all the F# gurus are also Haskell gurus, or at least Haskell aficionados, and a lot of people use Haskell for prototyping new languages (including DSLs). Any search for materials on F# or DSLs likewise leads to information on Haskell.

So I decided to learn me some Haskell.

The first thing I did was to get a working build of the GHC (Glasgow Haskell Compiler) by following the appropriate links at the official Haskell site. Next, I found some learning materials, including Real-World Haskell, Learn You a Haskell for Great Good, Haskell Wikibook, Planet Haskell, and Erik Meijer's excellent Channel 9 series. (The latter is worth watching for the shirts alone!)

And then, a couple of days ago, I got to work learning. And so far…

…so good. This is a really fun language, and fairly easy to learn if you already have some experience in F#. At least, knowing F# gets you past the initial strangeness of concepts like currying, pattern matching, etc. In fact, knowing F# is a tiny bit of an impediment because the syntax is so similar, the differences are difficult to remember. But on balance, knowing F# has helped a bunch. The two languages aren’t exactly siblings, but they are cousins, and I think learning the more formal environment of Haskell will help me with the more pragmatic environment of F#.

In order to illustrate the two together, below I present “A tiny corner of Lisp…” in both F# and in Haskell. This being a little fragment of Lisp functionality coded as identically as possible in the two languages. As always, the code and information here are presented "as-is" and without warranty or implied fitness of any kind; use at your own risk. (And since I don’t yet have anything that can syntax highlight Haskell, I deliberately did not syntax highlight the F#.)

// A tiny corner of Lisp in F#.

type Node =
| Atom of string
| Cons of Node*Node
| Nil

let car = function
| Cons(h,_) -> h
| _ -> failwith "Invalid 'car' target."

let cdr = function
| Cons(_,t) -> t
| _ -> failwith "Invalid 'cdr' target."

let rec append nCar nCdr =
match nCar with
| Cons(h,t) -> Cons(h, append t nCdr)
| Nil -> nCdr
| _ -> failwith "Invalid 'append' target(s)."

// Examples.

let a0 = Atom "a" // a
let c0 = Cons(a0,(Cons((Atom "b"),Nil))) // (a b)
let c1 = append c0 c0 // (a b a b)
let c2 = append c1 a0 // (a b a b . a)

-- A tiny corner of Lisp in Haskell.

data Node = Atom String
| Cons Node Node
| Nil
deriving (Show)

car :: Node -> Node
car (Cons h _) = h
car _ = error "Invalid 'car' target."

cdr :: Node -> Node
cdr (Cons _ t) = t
cdr _ = error "Invalid 'cdr' target."

append :: Node -> Node -> Node
append (Cons h t) nCdr = (Cons h (append t nCdr))
append Nil nCdr = nCdr
append _ _ = error "Invalid 'append' target(s)."

-- Examples.

a0 = Atom "a" -- a
c0 = Cons a0 (Cons (Atom "b") Nil) -- (a b)
c1 = append c0 c0 -- (a b a b)
c2 = append c1 a0 -- (a b a b . a)

So get out there and learn you a Haskell for great good!



Frank de Groot said...

Thanks for the links! Haskell is definitely on my packed list of stuff to learn.

Too bad Erik Meijer's dutch accent is HUGE. I'm dutch myself so it makes me cringe.

TechNeilogy said...

Thanks for posting, Frank! Like you, it took a while for Haskell to make it to the front of my queue. In part, it was Erik Meijer's enthusiastic videos that finally convinced me, lol.