I asked a question about Currying and closures were mentioned. What is a closure? How does it relate to currying?
相关问题
- Relation between Function1 and Reader Monad
- scala passing function with underscore produces a
- Combining n vectors into one vector of n-tuples
- Improve this code by eliminating nested for cycles
- Redefine list monad instance
相关文章
- Is there something like the threading macro from C
- Learning F#: What books using other programming la
- Creating a list of functions using a loop in R
- What does “exposition only” mean? Why use it?
- When to use interfaces, and when to use higher ord
- Functors in Ocaml
- Java Lambda Referencing Enclosing Object: Replace
- “Adapter” or “adaptor”?
Here's a real world example of why Closures kick ass... This is straight out of my Javascript code. Let me illustrate.
And here's how you would use it:
Now imagine you want the playback to start delayed, like for example 5 seconds later after this code snippet runs. Well that's easy with
delay
and it's closure:When you call
delay
with5000
ms, the first snippet runs, and stores the passed in arguments in it's closure. Then 5 seconds later, when thesetTimeout
callback happens, the closure still maintains those variables, so it can call the original function with the original parameters.This is a type of currying, or function decoration.
Without closures, you would have to somehow maintain those variables state outside the function, thus littering code outside the function with something that logically belongs inside it. Using closures can greatly improve the quality and readiblity of your code.
To help facilitate understanding of closures it might be useful to examine how they might be implemented in a procedural language. This explanation will follow a simplistic implementation of closures in Scheme.
To start, I must introduce the concept of a namespace. When you enter a command into a Scheme interpreter, it must evaluate the various symbols in the expression and obtain their value. Example:
The define expressions store the value 3 in the spot for x and the value 4 in the spot for y. Then when we call (+ x y), the interpreter looks up the values in the namespace and is able to perform the operation and return 7.
However, in Scheme there are expressions that allow you to temporarily override the value of a symbol. Here's an example:
What the let keyword does is introduces a new namespace with x as the value 5. You will notice that it's still able to see that y is 4, making the sum returned to be 9. You can also see that once the expression has ended x is back to being 3. In this sense, x has been temporarily masked by the local value.
Procedural and object-oriented languages have a similar concept. Whenever you declare a variable in a function that has the same name as a global variable you get the same effect.
How would we implement this? A simple way is with a linked list - the head contains the new value and the tail contains the old namespace. When you need to look up a symbol, you start at the head and work your way down the tail.
Now let's skip to the implementation of first-class functions for the moment. More or less, a function is a set of instructions to execute when the function is called culminating in the return value. When we read in a function, we can store these instructions behind the scenes and run them when the function is called.
We define x to be 3 and plus-x to be its parameter, y, plus the value of x. Finally we call plus-x in an environment where x has been masked by a new x, this one valued 5. If we merely store the operation, (+ x y), for the function plus-x, since we're in the context of x being 5 the result returned would be 9. This is what's called dynamic scoping.
However, Scheme, Common Lisp, and many other languages have what's called lexical scoping - in addition to storing the operation (+ x y) we also store the namespace at that particular point. That way, when we're looking up the values we can see that x, in this context, is really 3. This is a closure.
In summary, we can use a linked list to store the state of the namespace at the time of function definition, allowing us to access variables from enclosing scopes, as well as providing us the ability to locally mask a variable without affecting the rest of the program.
In a normal situation, variables are bound by scoping rule: Local variables work only within the defined function. Closure is a way of breaking this rule temporarily for convenience.
in the above code,
lambda(|n| a_thing * n}
is the closure becausea_thing
is referred by the lambda (an anonymous function creator).Now, if you put the resulting anonymous function in a function variable.
foo will break the normal scoping rule and start using 4 internally.
returns 12.
Variable scope
When you declare a local variable, that variable has a scope. Generally, local variables exist only within the block or function in which you declare them.
If I try to access a local variable, most languages will look for it in the current scope, then up through the parent scopes until they reach the root scope.
When a block or function is done with, its local variables are no longer needed and are usually blown out of memory.
This is how we normally expect things to work.
A closure is a persistent local variable scope
A closure is a persistent scope which holds on to local variables even after the code execution has moved out of that block. Languages which support closure (such as JavaScript, Swift, and Ruby) will allow you to keep a reference to a scope (including its parent scopes), even after the block in which those variables were declared has finished executing, provided you keep a reference to that block or function somewhere.
The scope object and all its local variables are tied to the function and will persist as long as that function persists.
This gives us function portability. We can expect any variables that were in scope when the function was first defined to still be in scope when we later call the function, even if we call the function in a completely different context.
For example
Here's a really simple example in JavaScript that illustrates the point:
Here I have defined a function within a function. The inner function gains access to all the outer function's local variables, including
a
. The variablea
is in scope for the inner function.Normally when a function exits, all its local variables are blown away. However, if we return the inner function and assign it to a variable
fnc
so that it persists afterouter
has exited, all of the variables that were in scope wheninner
was defined also persist. The variablea
has been closed over -- it is within a closure.Note that the variable
a
is totally private tofnc
. This is a way of creating private variables in a functional programming language such as JavaScript.As you might be able to guess, when I call
fnc()
it prints the value ofa
, which is "1".In a language without closure, the variable
a
would have been garbage collected and thrown away when the functionouter
exited. Calling fnc would have thrown an error becausea
no longer exists.In JavaScript, the variable
a
persists because the variable scope is created when the function is first declared and persists for as long as the function continues to exist.a
belongs to the scope ofouter
. The scope ofinner
has a parent pointer to the scope ofouter
.fnc
is a variable which points toinner
.a
persists as long asfnc
persists.a
is within the closure.