|
|
In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables. The explicit use of closures is associated with functional programming and with languages such as ML and Lisp. Constructs such as objects in other languages can also be modeled with closures. In some languages, a closure may occur when a function is defined within another function, and the inner function refers to local variables of the outer function. At runtime, when the outer function executes, a closure is formed, consisting of the inner function’s code and references to any variables of the outer function required by the closure. A closure can be used to associate a function with a set of "private" variables, which persist over several invocations of the function. The scope of the variable encompasses only the closed-over function, so it cannot be accessed from other program code. However, the variable is of indefinite extent, so a value established in one invocation remains available in the next. As a consequence, closures can be used to hide state, and thus to implement object-oriented programming. The term closure is often mistakenly used to mean anonymous function. This is probably because most languages implementing anonymous functions allow them to form closures and programmers are usually introduced to both concepts at the same time. These are however distinct concepts. The concept of closures was developed in the 1960s and was first fully implemented as a language feature in the programming language Scheme. Since then, many languages have been designed to support closures. Function objects are sometimes also called closures.
Closures and first-class functionsClosures 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 Scheme function: ; Return a list of all books with at least THRESHOLD copies sold. (define (best-selling-books threshold) (filter (lambda (book) (>= (book-sales book) threshold)) book-list)) In this example, the lambda expression The closure is then passed to the Here is the same example rewritten in ECMAScript (JavaScript), another popular language with support for closures: // Return a list of all books with at least 'threshold' copies sold. function bestSellingBooks(threshold) { return bookList.filter( function(book) { return book.sales >= threshold; } ); } The A function may create a closure and return it. The following example is a function that returns a function. In Scheme: ; Return a function that approximates the derivative of f ; using an interval of dx, which should be appropriately small. (define (derivative f dx) (lambda (x) (/ (- (f (+ x dx)) (f x)) dx))) In ECMAScript: // Return a function that approximates the derivative of f // using an interval of dx, which should be appropriately small. function derivative(f, dx) { return function(x) { return (f(x + dx) - f(x)) / dx; }; } Because the closure in this case outlives the scope of the function that creates it, the variables While this is not always clarified, a closure need not be formed using an anonymous function. The Python programming language, for example, has very limited support for anonymous functions but fully supports closures. For example, one way the above ECMAScript example could be implemented in Python is: # Return a function that approximates the derivative of f # using an interval of dx, which should be appropriately small. def derivative(f, dx): def gradient(x): return (f(x + dx) - f(x)) / dx return gradient In this example, the function named gradient forms a closure together with the variables f and dx. This closure is then returned by the outer function named derivative. In fact, closures in Python must often be formed using named functions, where an anonymous function might be equally appropriate in other languages, because of the restrictions on lambda forms[2]. Uses of closuresClosures have many uses:
In Scheme (define foo #f) (define bar #f) (let ((secret-message "none")) (set! foo (lambda (msg) (set! secret-message msg))) (set! bar (lambda () secret-message))) (display (bar)) ; prints "none" (newline) (foo "meet me by the docks at midnight") (display (bar)) ; prints "meet me by the docks at midnight" Note: Some speakers call any data structure that binds a lexical environment a closure, but the term usually refers specifically to functions. Differences in semanticsAs different languages do not always have a common definition of the lexical environment, their definitions of closure may vary as well. The commonly held minimalist definition of the lexical environment defines it as a set of all bindings of variables in the scope, and that is also what closures in any language have to capture. It should be noted though that the meaning of a variable binding also differs. In imperative languages, variables bind to relative locations in memory that can store values. Although the relative location of a binding does not change at runtime, the value in the bound location can. In such languages, since closure captures the binding, any operation on the variable, whether done from the closure or not, is performed on the same relative memory location. Here is an example illustrating the concept in ECMAScript, which is one such language: var f, g; function foo() { var x = 0; f = function() { return ++x; }; g = function() { return --x; }; x = 1; print(f()); // "2" } foo(); print(g()); // "1" print(f()); // "2" Note how function On the other hand, many functional languages, such as ML, bind variables directly to values. In this case, since there is no way to change the value of the variable once it is bound, there is no need to share the state between closures - they just use the same values. Yet another subset, lazy functional languages such as Haskell, bind variables to a result of a computation in the future. Consider this example in Haskell: foo x y = let r = x / y in (\z -> z + r) f = foo 1 0 main = do putStr (show (f 123)) The binding of Yet more differences manifest themselves in the behavior of other lexically scoped constructs, such as "Smalltalk" foo | xs | xs := #(1 2 3 4). xs do: [:x | ^x]. ^0 bar Transcript show: (self foo) "prints 1" // ECMAScript function foo() { var xs = new Array(1, 2, 3, 4); xs.forEach(function(x) { return x; }); return 0; } print(foo()); // prints 0 The above code snippets will behave differently because the Smalltalk Common Lisp provides a construct that can express either of the above actions: Smalltalk
foo
^[ :x | ^x ]
bar
| f |
f := self foo.
f value: 123 "error!"
When the closure returned by the method Some languages, such as Ruby, allow the programmer to choose the way he wants # ruby def foo f = Proc.new { return "return from foo from inside proc" } f.call # control leaves foo here return "return from foo" end def bar f = lambda { return "return from lambda" } f.call # control does not leave bar here return "return from bar" end puts foo # prints "return from foo from inside proc" puts bar # prints "return from bar" Both In Scheme, definition and scope of the (define call/cc call-with-current-continuation) (define (foo) (call/cc (lambda (return) (define (f) (return "return from foo from inside proc")) (f) ; control leaves foo here (return "return from foo")))) (define (bar) (call/cc (lambda (return) (define (f) (call/cc (lambda (return) (return "return from lambda")))) (f) ; control does not leave bar here (return "return from bar")))) (display (foo)) ; prints "return from foo from inside proc" (newline) (display (bar)) ; prints "return from bar" Implementation and theoryClosures 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 (e.g., the set of available variables and their values) at the time when the closure was created. 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. The alternative is for the language to accept that certain use cases will lead to undefined behaviour, as in the proposal for lambda expressions in C++.[1] In ML, local variables are allocated on a linear stack. When a closure is created, it copies the values of those variables that are needed by the closure into the closure's data structure. A typical modern Scheme implementation allocates local variables that might be used by closures dynamically and stores all other local variables on the stack. 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.[2] See also
References
External links
CommentsNo comments have been added. |
Popular PagesEmail this Page |