Closure (computer science)
In
programming languages, a
closure is a
function that refers to
free variables in its lexical context.
A closure typically comes about when one function is declared entirely within the body of another, and the inner function refers to local variables of the outer function. At
run time, when the outer function executes, a closure is formed. It consists of the inner function's code and references to any variables in the outer function's scope that the closure needs.
Closures are commonly used in
functional programming to defer calculation, to
hide state, and as arguments to
higher-order functions.
A closure combines the code of a function with a special lexical environment bound to that function (
scope). Closure lexical variables differ from
global variables in that they do not occupy the global variable
namespace. They differ from
object oriented member variables in that they are bound to function invocations, not object instances.
Closures are typically implemented with a special
data structure that contains a pointer to the function code, plus a representation of the function's lexical environment (i.e., the set of available variables and their values) at the time when the function was created.
Closures are closely related to Actors in the
Actor model of concurrent computation where the values in the function's lexical environment are called
acquaintances. An important issue for closures in
concurrent programming languages is whether the variables in a closure can be updated and if so how these updates can be synchronized. Actors provide one solution (Will Clinger
1981).
Closures typically appear in languages in which functions are
first-class values—in other words, such languages allow functions to be passed as arguments, returned from function calls, bound to variable names, etc., just like simpler types such as strings and integers.
For example, consider the following
Lisp function:
(defun best-selling-books (threshold)
"Return a list of all books with at least THRESHOLD copies sold."
(filter
#'(lambda (book) (>= (book-sales book) threshold)) *book-list*))
Here the lambda expression
(lambda (book) (>= (book-sales book) threshold)) appears within the function
best-selling-books. When the lambda expression is evaluated, Lisp creates a closure consisting of the code for the lambda and a reference to the
threshold variable, which the lambda uses.
The closure is then passed to the
filter function, which calls it repeatedly to determine which books are to be added to the result list and which are to be discarded. Because the closure itself has a reference to
threshold, it can use that variable each time
filter calls it.
filter might be defined in a completely separate file.
A function may create a closure and return it. The following example is a function that returns a function.
(defun derivative (f dx)
"Return a function that approximates the derivative of f
using an interval of dx, which should be appropriately small."
#'(lambda (x) (/ (- (funcall f (+ x dx)) (funcall f x))
dx)))
Because the closure in this case outlives the scope of the function that creates it, the variables
f and
dx live on after the function
derivative returns. In languages without closures, the lifetime of a local variable coincides with the execution of the scope where that variable is declared. In languages with closures, variables continue to exist as long as any existing closures have references to them.
Closures have many uses:
* Designers of
software libraries can allow users to customize behavior by passing closures as arguments to important functions. For example, a function that
sorts values can accept a closure argument that compares the values to be sorted according to a user-defined criterion.
* Because closures delay evaluation—i.e., they do not "do" anything until they are called—they can be used to define control structures. For example, all Smalltalk's standard control structures, including branches (if/then/else) and loops (while and for), are defined using objects whose methods accept closures. Users can easily define their own control structures as well.
* Multiple functions can be produced which close over the same environment, enabling them to communicate privately by altering that environment.
* Closures are sometimes
better alternatives to objects.
Note: Some speakers call any data structure that binds a lexical environment a closure, but the term usually refers specifically to functions.
Scheme was the first programming language to have fully general, lexically scoped closures. Virtually all
functional programming languages, as well as the
Smalltalk-descended
object-oriented programming languages, support some form of closures.
Eiffel has a notion of agent. An agent is an object wrapping a routine, with some arguments possibly evaluated ("closed") and others left for evaluation at the time of call ("open"). Eiffel agents are extensively used in GUI programming, numerical applications, database manipulation, and reflection.
Though semantics vary greatly, many modern, general-purpose programming languages have lexical scoping and some variation on closures.
In
C, libraries that support
callbacks sometimes allow a callback to be registered using two values: a function pointer and a separate
void * pointer to arbitrary data of the user's choice. Each time the library executes the callback function, it passes in the data pointer. This allows the callback to maintain state and to refer to information captured at the time it was registered. The idiom is similar to closures in functionality, but not in syntax.
Several object-oriented techniques and language features simulate some features of closures. For example:
*
Eiffel includes
inline agents defining closures. An inline agent is an object representing a routine, defined by giving the code of the routine in line. For example, in
OK_button.click_event.subscribe (
agent (x, y: INTEGER)
do country := map.country_at_coordinates (x, y) ; country.display
end )
the argument to `subscribe' is an agent, representing a procedure with two arguments; the procedure finds the country at the corresponding coordinates and displays it. The whole agent is `subscribed' to the event type `click_event' for a certain button, so that whenever an instance of the event type occurs on that button â€" because a user has clicked the button â€" the procedure will be executed with the mouse coordinates being passed as arguments for x and y.
*
C++ allows defining
function objects by overloading
operator(). These objects behave somewhat like functions in a functional programming language. They may be created at runtime and may contain state. But, they do not implicitly capture local variables as closures do. Two proposals to introduce C++ language support for closures (both proposals call them lambda functions) are being considered by the C++ Standards Committee [
1], [
2]. The main difference between these proposals is that one stores a copy of all the local variables in a closure by default, and another stores references to original variables. Both provide functionality to override the default behaviour. If some form of these proposals is accepted, one would be able to write
void foo(string myname) { typedef vector
names; int y;
names n;
// ...
names::iterator i =
find_if(n.begin(), n.end(), <>(const string& s){return s != myname && s.size() > y;}); // i is now either n.end() or points to the first string in n // which is not equal to myname and which length is greater than y }
* Java allows defining "anonymous classes" inside a method; an anonymous class may refer to names in lexically enclosing classes, or final variables in the lexically enclosing method.
class CalculationWindow extends JFrame { private JButton btnSave;
...
public final calculateInSeparateThread(final URI uri) {
// The expression "new Runnable() { ... }" is an anonymous class.
Runnable runner = new Runnable() {
void run() {
// It can access final local variables:
calculate(uri);
// It can access private fields of the enclosing class:
btnSave.setEnabled(true);
}
};
new Thread(runner).start();
}
}
* In C# the anonymous functions [3] look and behave much like closures. This works only for reference types. Value types (such as System.Int32, System.DateTime and anything descending from System.ValueType) are copied, not referenced, which still can be used for 'read-only' types of closures (where changes should not be reflected in the original scope).A language implementation cannot easily support full closures if its run-time memory model allocates all local variables on a linear stack. In such languages, a function's local variables are deallocated when the function returns. However, a closure requires that the free variables it references survive the enclosing function's execution. Therefore those variables must be allocated so that they persist until no longer needed. This explains why typically languages that natively support closures use garbage collection.
A typical modern Scheme implementation allocates local variables that might be used by closures dynamically and stores all other local variables on the stack.* Funarg problem
* Continuation
* Spaghetti stack
* Value-level programming
* Command pattern
* Eiffel programming language*Will Clinger. [https://dspace.mit.edu/handle/1721.1/6935 Foundations of Actor Semantics]. MIT Mathematics Doctoral Dissertation. June 1981.*The Original "Lambda Papers": A classic series of papers by Guy Steele and Gerald Sussman discussing, among other things, the versatility of closures in the context of Scheme (where they appear as lambda expressions).
*Agents in Eiffel, also latest version in Eiffel standard.
*Description from the Portland Pattern Repository
*"Achieving Closure": A tutorial on closures in Perl by Simon Cozens.
*Description from the VisualWorks wiki
*Closures by the Spirit library of Boost
*Description from the GObject Reference Manual
*Javascript Closures
*Boo Closures
*Python closures
*PHP Closures
*Sleep Closures
*Syntactic Closures: A 1988 paper, by Alan Bawden and Jonathan Rees, that discusses a special use of closures in LISP syntax macros
*Martin Fowler's Bliki: Closure
*C# Anonymous methods