It was thus said that the Great Liam Proven once stated:
I use this all
the time in Python; great for generating tiny local
functions to thunk a string into the right format, too.
Thanks for the example, but I am afraid I can't follow a line of it.
Sorry. I have tried to cram some Python into my head several times,
but it won't stick.
I sent this on January 5th directly to Liam, but I never heard back. I
suppose it might have gone into some spam bucket, or just overlooked.
Anyway, here's what I originally sent.
--- original message ---
It was thus said that the Great Liam Proven once stated:
Closures: again, I have yet to find a readable, comprehensible
explanation of what they are, what they are good for, why they are
powerful and what strengths they confer onto a programming language.
Understanding closures requires one understands lexical scoping of
variables, and that functions can be passed and returned, just like any
other type of variable (like an integer or string).
I'm going to use Lua here, as it's a simple langauge and should be easy to
follow. First, a regular function:
function add(x,y)
return x + y
end
Tis a silly function, doesn't do much. But in Lua (and basically, any
langauge that supports closures), you can assign the function to another
variable.
myfunction = add
And from there:
x = add(1,2)
y = myfunction(1,2)
x and y contain the same value. In fact, you can rewrite the add()
function as:
add = function(x,y)
return x + y
end
And even pass a function to another function:
function do_something(f,v1,v2)
return f(v1,v1 - v2)
end
Or even return a function, previously defined or not:
function whichone(name)
if name == 'plus' then
return add
elseif name == 'minus' then
return function(x,y) return x - y end
else
return function(x,y) error("bad function name") end
end
end
myadd = function('plus')
x = add(1,2)
y = myadd(1,2)
So, functions can be treated like any other type of value, such as
integers or strings. Now, what about this function?
function adder(increment)
return function(x) return add(x,increment) end
end
This returns a function that adds a set amount to its argument. So we
have:
a5 = adder(5)
x = a5(1)
print(x)
6
It works, but why? Once adder() is finished, there shouldn't be an
"increment" variable anywhere ... it was a parameter! It's a variable with
a supposedly empherial existence, only living while the function is running.
Except, we aren't exactly returning a function, but what's called a
"closure". The technical terminology is something like "it closes over
the
lexical scope of all the variables" but really, it's two items treated as a
single entity: the function (really, a pointer to a function) and a copy of
any variable outside the functions scope used by that variable.
Another silly example:
function silly(a,b)
local count = 0
return function(x)
count = count + 1
print("I've been called ",count,"times")
return a * x + b
end
end
myfunc = silly(5,3)
x = myfunc(1)
Here, a, b, and count are copied to the "data portion" of the closure,
which is returned along with the (pointer to a) function. And yes, the
returned function can modify the "variables" in the closure, but since
they're "local" to the function, it's okay. A C-type representation of
this
would be something like:
typedef int (*function__t)(int);
typedef struct
{
function__t code;
struct { int a; int b; int count; } data;
} closure__t;
static int __internal_function(closure__t *self,int x)
{
self->data.count++;
printf("I've been called %d times\n",self->data.count);
return self->data.a * x + self->data.b;
}
closure__t silly(int a,int b)
{
closure__t *cl;
cl = malloc(sizeof(closure));
cl->code = __internal_function;
cl->data.a = a;
cl->data.b = b;
cl->data.count = 0;
return cl;
}
/* ... */
closure__t *myfunc;
int x;
myfunc = silly(5,3);
x = (*myfunc->code)(myfunc,1);
Only all these gritty details are hidden and done automatically by the
compiler/interpreter/what have you.
And that's really what a closure is. What can it be used for? Well, in
Lua, closures are used in for() loops. For instance, I have some Lua code
that wraps the Unix calls opendir() (to open a directory) and readdir() (to
read the next entry in the directory) and one can loop through a directory
like:
d = fsys.opendir(".") -- current directory
while true do
entry = fsys.readdir(d)
if entry == nil then break end
print(entry)
end
But given how Lua implements a for() loop, we can write:
function dir(name)
local function internal_dir(state)
return fsys.readdir(state)
end
local directory = fsys.opendir(name)
return internal_dir,directory -- for() needs these two values
end
And now we can do:
for entry in dir(".") do
print(entry)
end
That's just one example, in one language, of a closure. It can make some
constructs easier and prettier. Why such stuff has to be jargoned up is
beyond me.
-spc (Well, I guess that's enough procrastination; back to the saltmines)