Page 3: Closure
The nested scoping rules become more interesting when we start passing around those functions we create. We’ll still use boring toy programs for this page. We’ll get to a real application soon.
Closure 1
Here’s a simple function inside of a function:
|
|
Some things to note here:
- the function
f
returns the function object that was stored ing
(line 7) - the function
g
is defined inside off
(lines 3-6) - the variable
a
used inside ofg
is the one defined on line 2 - the variable
b
is assigned the result off
(which was the function formerly known asg
) - the function stored in
b
is called twice (on lines 10 and 11)
Hopefully, it makes sense that this program will print 1 then 2. When we call the function stored in b
, we are executing the function defined on lines 3-6 (which access the a
variable, defined on line 2). The first time we call it, it increments a
(which is 0, so it will get the value 1) and prints it (so it prints 1). The second time we call it, it increments a
(which is 1, so it will get the value 2) and prints it.
The thing to observe: the function object not only contains the code (lines 3-6) it also “encloses” the variable defined on line 2.
Two Closures
OK, Same function, but we will use it twice
|
|
If you understood the previous example, you should see that the two calls to b
(lines 11 and 12) will print 1 and 2 respecitvely. Figuring out what line 13 prints is a little trickier, and requires us to really understand what the closure does.
On line 9, we call the function f
which uses the function
keyword to create a “function object” that encloses the a
variable defined on line 2.
On line 10, we call the function f
again, which repeats that process. This second time we call f
, we get a “new” a
variable (every time we call a function, we get a new “copy” of its local variables). When we call the function
keyword, that creates another function object, which encloses that new a
variable.
So, by calling the f
function twice, we’ve created two different function objects (you can think of it as if we ran the compile to make a new function object twice, although the compiler really doesn’t run twice). Each of those function objects encloses a different version of the a
variable.
So, line 13 prints 1. It is using the second function object. The a
inside of the first function object was incremented twice already (on lines 11 and 12). But the a
inside of the second function object (stored in the c
variable) is a different a
- it started from zero.
That’s closure.
Closures with Arguments
Closure applies to function arguments as well - they are just like any other variables.
Here’s a toy example:
|
|
The adder
function is a function that returns a function that adds an amount to its argument. Adder takes one argument x
. It returns a function that takes one argument y
. That function adds x
and y
together.
On line 4, we set a1
to be the function that adds its argument y
to the value of x
that is enclosed in it. In this case, x
was 1 (the value of the parameter to adder
).
On line 5, we call adder
again, to have it produce another function object. This time enclosing a different copy of x
that happens to be set to 3.
In this case, x
isn’t going to change (each different version of x
is different, but those versions don’t change their values). But it is important to remember that closures enclose the variable, not the value.
Beyond toy examples
Hopefully, these simple examples get the concepts of a closure across. We create function objects than “enclose” the variables around them.
Closure can be a convenient way to write programs where we need to create functions (for example handler functions) that have private data. There are other ways to program these things, but once you understand closures, they are a useful tool.
On the next page we’ll look at a more realistic example.
Next: Closure Example