Haskell and CoffeeScript – How CoffeeScript is Influenced by Haskell

coffeescripthaskell

I've been using CoffeeScript for a while now. On Wikipedia, it is said that CoffeeScript is influenced by Haskell. But after I check out the syntax of Haskell, I have found little resemblance from CoffeeScript.

Which aspect of CoffeeScript is influenced by Haskell?

Best Answer

Coffeescript is a gateway drug for Haskell. Let's take a look at the Quicksort algorithm, as presented in Chapter Five of Learn You A Haskell For Great Good:

quicksort [] = []
quicksort (x:xs) =
       let smallerOrEqual = [a | a <- xs, a <= x]
           larger = [a | a <- xs, a > x]
       in quicksort smallerOrEqual ++ [x] ++ quicksort larger

And the equivalent in Coffeescript:

quicksort = (x) ->
    return [] if x.length == 0
    h = x.pop()
    smallerOrEqual = (a for a in x when a <= h)
    larger = (a for a in x when a > h)
    (quicksort smallerOrEqual).concat([h]).concat(quicksort larger)

The only obvious difference between these two bits of code is that the pattern had to be turned into a guard condition.

These are not equivalent pieces of code. The Haskell code will only do the list comprehensions lazily, i.e. when needed by the in line. Pattern recognition is a big deal in Haskell. Haskell will optimize this code with tail-recursion, whereas Coffeescript will just build a framestack as big as the number of recursions needed. And the Haskell version cares about the contents of the list and demands they be all of the same type, since Haskell is so strongly typed.

But one could be fooled, by their clear syntactical resemblence, into believing they were equivalent. And to some extent, a smart developer could use that resemblence to write Coffeescript that, if not lazy or strongly typed, was nonetheless better disciplined and more functional. Haskell is an excellent language for learning that discipline, and those skills are directly translatable to Coffeescript's syntax.

[EDIT] Building on Linus's excellent addition below, we can even simulate Haskell laziness:

quicksort = ([x, xs...]) ->
    return [] unless x?
    smallerOrEqual = -> (a for a in xs when a <= x)
    larger = -> (a for a in xs when a > x)
    (quicksort smallerOrEqual()).concat(x).concat(quicksort larger())

Now the two expressions only trigger when their results are needed, not just imperatively. I doubt this actually wins you anything, but it does seem to be getting closer to some Haskellian purity.

Related Topic