Quick, what's the difference between these two expressions?
// Javascript
var add21a = function(a) { return a + 21; };
var add21b = (function(b) {
return function(a) { return b + a };
})(21);
// Coffeescript
add21a = (a) -> a + 21
add21b = ((b) -> (a) -> b + a)(21)
When I first encountered functional programming, I internalized the definition of a pure function: A pure function is one that always returns the same result given the same arguments. You should always write your code in a transformative way: Given a set of inputs, write the toolchain to unambiguously create a set of outputs and, only then, perform the side-effect of updating your presentation to reflect your result.
It wasn't until I was knee-deep into a program with a lot of functions-writing-functions that I stopped to ask myself the question posed above. I was peeling off dozens of functions that, themselves, were created with closures that retained a lot of state, were themselves mapped to various inputs into the system, and were themselves responsible for producing the output.
The magic is that each individual function was pure: it always gave the same result given the same arguments. But each function itself was created by a pure function that, given the same arguments, produced the same pure function. So, was the entire result pure?
The answer, apparently, is yes. The answer to my question above is, there is no difference.
On the one paw, I understand the point being made here: I can reason about my code much more rationally. The products of each step are smaller, more reasonable, and generally easier to comprehend, so both ensuring that the code is correct, and debugging it, are facilitated by the functional approach. On the other paw, this approach allows one to create a vast array of functions rather than a large object with a single function and a vast array of data, with the possibility of code sprawl now simply existing in a different form.
I like functional programming. It fits my brain, as the Python folks say. But I don't think we should at all kid ourselves about the complexity of writing software, or the possibility of anti-patterns emerging out of functional programming. I'd be happy to say that there are fewer anti-patterns in languages like Haskell or Coffeescript, but the ones left behind are much harder to manage, avoid, and debug.