Wednesday, June 18, 2008

PE Problem #1 in Haskell

Analysis of Haskell

Haskell is a purely functional programming language which means that everything is computed with functions—no side effects! Mostly this comes down to no assignment statement, and it does pose some challenges for I/O.

Haskell also uses lazy evaluation—don't evaluate a computation unless it's actually needed. This makes for some interesting debugging sessions.

Another key aspect of Haskell is its type system. It is strongly typed so that as many errors are caught by the compiler; it also uses type inference so that programmers don't have to be explicit about the types all of the time—the compiler figures it out on its own (mostly).

I played around with Haskell in my early graduate-school days; I even got a tech report out of it: "Matrix Inversion using Quadtrees Implemented in Gofer" (Gofer being a precursor to Hugs, a Haskell interpreter). I mostly remember fighting with the type system and a little bit of appreciation for its mathematical purity.

Unfortunately, my research forced me away from Haskell soon after that report, and I was stuck it icky C until I defended.

Terse and Expressive

Consider this definition of an "interesting number":

interestingNumber n = mod n 3 == 0 || mod n 5 == 0

It's fairly readable, although it does seem a bit fragile to me. I think part of that feeling comes from the fact that this is strongly typed, even though I haven't said anything about n's type.

Although, that's not really true. I have said something about n's type. n can be the first argument to mod when the second argument is 3 and when it's 5. So Haskell infers a type for the function:

interestingNumber :: (Integral a) => a -> Bool

Now at this point I don't pretend to fully understand what Haskell means by "Integral", but presumably this is some integer-like type class. The context (Integral a) just says that the type variable a must come from (i.e., implement) the Integral type class.

So, as long as a value for n comes from an Integral type, it can be the first argument to interestingNumber, and the function will return a boolean value.

But keep in mind that I asked Haskell (actually, ghci) to give me the type signature of interestingNumber. I never typed in that signature myself.

sum35 is even more awesome:

sum35 n = sum [m | m <- [0..n], interestingNumber m]

How cool is that list comprehension!?!! It's what a mathematician would write.

One of the really cool things here is that Haskell gets to determine how to execute this code—a loop, recursion, a lookup table, whatever! It could even decide to compute this in parallel. Oooo!

Next Language...

I think I'll next tackle Dylan. Stay tuned!

3 comments:

Dave Brondsema said...

Not that I'm following your posts to the utmost detail, but I thought I'd let you know I'm enjoying reading them.

Also, I was prepping for the GR-JUG meeting tomorrow and went to the ANTLR site. And what do you know? There's a prominent link to your ANTLR testing project with your name underneath. Nice work :)

Ray Myers said...

sum35 n = sum $ filter interestingNumber [0..n]

However, I agree that list comprehensions are a very useful bit of syntax. I'm still trying to grok the generalization: monad comprehensions.

Jeremy D. Frens said...

@dave: Not to rain on my own parade with regards to the the ANTLR Testing on the ANTLR website, I actually posted that news item myself. I'm a bit surprised it's still at the top of that list, though.

@ray: I think the $ was added to Haskell after I stopped using it. I'm going read more about it since I see it popping up several places. In this case, I still like the list comprehension a bit better.