Closure (computer programming)
In programming languages, a closure is a function that refers to free variables in its lexical context.
A closure is usually a function created by a program at run time. This is best demonstrated by a function that appears entirely within the body of another function. The nested, inner function must refer to local variables of the outer function. As the outer function executes, it creates a new instance of the inner function. If that inner function refers to some of the local variables of the outer function, a closure is formed. It consists of the function code and a reference 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 object variables in that they are bound to functions, not objects.
Implementation and theory
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).
Examples
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.
Uses of closures
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.
Programming languages with closures
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.
Simulating 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:
- In C++, programmers may define 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. However, 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<string> 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 the programmer to define "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# and VB.Net, 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).
Implementation
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.
See also
Reference
- Will Clinger. Foundations of Actor Semantics. MIT Mathematics Doctoral Dissertation. June 1981.
External links
- 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).
- 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