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.
I think the Article has a point as far as JavaScript vs. CoffeScript is concerned.
I personally find JavaScript quite readable and I just do not see the point of sticking another layer of syntax on top.
I have similar experiences with Java/Groovy, Groovy is just great: highly expressive, cuts out a lot of useless tedious typing compared with Java, the "extras" like native SQL support are really worth having. BUT the last time I used it debugging was painful, you end up stepping through endless obscure internal Groovy classes before you get back to your own code.
Python on the other hand is a complete self supporting environment, it is it's own language and is not tacked on top of another language (although Python itself is written in C and has excellent integration with anything written in C or C++). It has its own debugger so for the most part you are debugging the python code you wrote.
The designers of Python obsess over the expressiveness of the language and consistency of syntax. Once you get the hang of it it is very readable. You genuinely write much less code in Python compared with using other languages to solve the same problem, and, well written Python code is clear and unambiguous.
The only downsides are that in common with most dynamic languages it does not play well with IDEs, and, all that lovely high level expressiveness is not interpreted into a lean mean execution.
Best Answer
CoffeeScript is inexorably tied to JavaScript, and JavaScript differentiates between the following expressions:
In fact, you can even write:
Since this difference matters in JavaScript, it makes sense to use the same terms when talking about CoffeeScript. However, CoffeeScript does not support anything like the
function foo ()
syntax, so we can say it does not have "named" functions.In a sense, the name is part of the function definition in the
function foo() { ... }
, where in the other case you just create a function and assign it to a variable. This is reflected, for example, in the (nonstandard)name
property of functions: in the first case,foo.name
will give you"foo"
and in the second it will give you""
.Additionally, in JavaScript, these also differ in terms of how they get introduced to the scope: the first version is "hoisted" and available throughout its scope where the second definition is only available after it's assigned.
Basically, just think of it as JavaScript-specific jargon, which is transferred to CoffeeScript because CoffeeScript is so closely related to JS.