The concept of a closure is not specific to functional programming. It simply means that you have:
- A variable in a scope
- Something constructed inside this scope (typically a function or an object) that uses said variable
- That something being passed to somewhere outside the originating scope
The textbook example is something like this:
function foo() {
var x = 23;
var bar = function(y) {
return x + y;
};
return bar;
}
...and we say that bar
closes over x
.
However, we can do the same with objects, really:
function foo() {
var x = 23;
var bar = {
"y": x
};
return bar;
}
The mechanism is the same, even though now it is not a function that closes over x
, but an object. It is not usually called a closure though, because unlike function closures, this kind of behavior is "obvious" for an imperative programmer, and the idea is that we're just referencing a local variable to put a value into an object -- but then, that's exactly what a closure does, only that the object can also be a function.
Now, as far as functional programming goes: The most important concept is the function. Unlike functions in imperative languages, for which "routine" or "procedure" is actually a much better name, functional-programming functions are conceptually like Mathematical functions: mappings from things to things. By taking this concept and using it as the first and foremost expressive primitive, the other hallmarks of functional programming follow logically:
- Functions map inputs to outputs; that is all they do. Side effects such as printing or maintaining mutable state don't fit this model, so functional programmers tend to avoid these things. This is what people call purity: a pure function is a function that does not have side effects. Different FP languages treat this matter differently; at the extreme end, there is Haskell, which does not allow any impure functions at all, while at the pragmatic end, there are Lisp, Scheme, JavaScript etc., which allow side effects to appear anywhere and leave their avoidance to boyscout programmers.
- Functions are things, too, so it makes sense to have functions that take functions as input, or return functions as output, or both. Such functions are known as higher-order functions, and their use is ubiquitous in functional programming. The famous
map
function is such a higher-order function: one of its arguments is a function, which map
applies to every element in the other argument (which is supposedly a list of some sort).
- Functions can be expressed in terms of themselves, a.k.a. recursion. Recursive definitions are easier to write in a pure fashion; they do not rely on mutable-state constructs such as loop counter variables. Because of this, functional programmers tend to prefer recursive solutions over iterative ones, and functional programming languages provide various optimizations to avoid the problems that recursive programming can cause (stack overflow, memory leaks, etc.). Further, common types of recursion are available in a generic fashion in every functional programming language worth the label, the most famous ones being
map
and reduce
(a.k.a. fold
). With the help of these functions, a functional programmer can abstract away the details of the actual recursion, and thinking in terms of map
, reduce
and filter
becomes second nature at some point.
- The preferred type of function is the unary function, which takes only one argument. Using closures, any n-ary function can be rewritten as a unary function that returns an (n-1)-ary function, and by fully applying this logic, any n-ary function can be written as a nested chain of unary functions returning the next link in the chain. This process is called currying, while the concept of calling a function with an incomplete set of arguments, yielding another function that takes the rest of the arguments, is known as partial function application. As a simple example, if you have a function that you can call like so:
foo(a, b, c)
, then the fully-curried version would be called as: foo(a)(b)(c)
; calling the curried foo like this: foo(a)(b)
constitutes partial application and yields a function which, when called with c
, gives the same result as the original call, e.g.: f = foo(a)(b); f(c)
.