Tuesday, January 16, 2007

Lexical closures in Common Lisp vs. Lua

I don't get to do too much with dynamic languages at work, so I was really happy to get a chance to write some Lua today. I wanted to make a function that would create a timer that would execute a callback when it was finished, and long story short, I started experimenting with closures to see exactly how they worked in Lua. I came across something that I didn't entirely expect. First, an example in CL:
(mapc (lambda (x) (funcall x))
(loop for i from 1 to 3 collect
(let ((localvar i))
(lambda ()
(format t "loop:~a, local:~a~%" i localvar)))))
This outputs:
loop:4, local:1
loop:4, local:2
loop:4, local:3
which makes sense to me, now that I've been working in Common Lisp. Inside the inner lambda, i refers to the loop variable, which is at 4 at the end of the loop. During each iteration of the loop, a new binding is created for localvar, and then the inner lambda closes over it. That's why we have different values for local but the same value for loop.

However, the closest equivalent I can manage in Lua is this (apologies for the temporary):
collection={}
for i=1,3 do
collection [i] = function ()
local localvar = i
print("loop:" .. i .. ", local:" .. localvar .. "\n")
end
end
for k,v in pairs (collection) do
v()
end
And this outputs:
loop:1, local:1
loop:2, local:2
loop:3, local:3
So, it looks like Lua actually closes over the value, whereas Lisp closes over the binding. I'm trying to think of a way to force Lua to close over the binding by boxing the value or something, but I'm coming up dry.

No comments: