There's a lot of confusion around here because there are terms with multiple definitions, and multiple distinct things that get conflated simply because they're usually found together.
First, we have "block". That's just a lexical chunk of code that makes a unit - the body of a loop, for instance. If the language actually has block scope, then variables can be defined that only exist within that chunk of code.
Second, we have callable code as a value type. In functional languages, these are function values - sometimes called "funs", "anonymous functions" (because the function is found in the value, not the name its assigned to; you don't need a name to call them), or "lambdas" (from the operator used to create them in Church's Lambda Calculus). They may be called "closures", but they aren't automatically true closures; in order to qualify, they must encapsulate ("close over") the lexical scope surrounding their creation - that is, variables defined outside the scope of the function itself but within the scope of its definition are still available whenever the function is called, even if the calling point is after the referenced variable would otherwise have gone out of scope and had its storage recycled.
For example, consider this Javascript:
function makeClosure() {
var x = "Remember me!";
return function() {
return "x='" + x + "'";
}
}
// console.log(x);
// The above is an error; x is undefined
var f = makeClosure();
console.log(f());
// The above outputs a string that includes x as it existed when f was created.
The variable x
is defined only within the body of the function makeClosure
; outside of that definition, it doesn't exist. After we call makeClosure
, the x
declared inside it should be gone. And it is, from the point of view of most of the code. But the function returned by makeClosure
was declared while x
existed, so it still has access to it when you call it later. That makes it a true closure.
You can have function values that are not closures, because they don't preserve scope. You can also have partial closures; PHP's function values only preserve specific variables that must be listed at the time the value is created.
You can also have callable code values that don't represent whole functions at all. Smalltalk calls these "block closures", while Ruby calls them "procs", though many Rubyists just call them "blocks", because they're the reified version of what's created by the {
...}
or do
...end
syntax. What makes them distinct from lambdas (or "function closures") is that they do not introduce a new call level. If the code in the body of a block closure invokes return
, it returns from the outer function/method the block closure exists within, not just the block itself.
That behavior is critical to preserving what R.D. Tennent labeled the "correspondence principle", which states that you should be able to replace any code with an inline function containing that code in the body and called immediately. For instance, in Javascript, you can replace this:
with this:
(function(){x = 2;})();
console.log(x)
That example is not very interesting, but the ability to do this sort of transformation without affecting the behavior of the program plays a key role in functional refactoring. But with lambdas, as soon as you have embedded return
statements, the principle no longer holds:
function foo1() {
if (1) {
return;
}
console.log("foo1: This should never run.")
}
foo1()
function foo2() {
if (1) {
(function() { return; })();
}
console.log("foo2: This should never run.")
}
foo2()
The second function is different from the first; the console.log
is executed, because the return
only returns from the anonymous function, not from foo2
. This breaks the correspondence principle.
This is why Ruby has both procs and lambdas, even though the distinction is a perennial source of confusion for newbies. Both procs and lambdas are objects of class Proc
, but they behave differently, as indicated above: a return
just returns from the body of a lambda, but it returns from the method surrounding a proc.
def test
p = proc do return 1 end
l = lambda do return 1 end
r = l[]
puts "Called l, got #{r}, still here."
r = p[]
puts "Called p, got #{r}, still here?"
end
The above test
method will never get to the second puts
, because the invocation of p
will cause test
to return immediately (with a return value of 1). If Javascript had block closures, you could do the same thing, but it doesn't (although there is a proposal to add them).
BlockClosure
. – Dereism