Consider this Lua 5.1 code:
function foo ()
function makeAdder (withWhat)
return function (a)
return a + withWhat
end
end -- makeAdder
f1 = makeAdder (6)
f2 = makeAdder (7)
end -- for
foo ()
print (f1 (2)) --> 8
print (f2 (2)) --> 9
The functions f1 and f2 have 6 and 7 as upvalues to the closures, and thus output 8 and 9 in response to being called. This is what I expect.
Now consider:
function foo ()
local withWhat
function makeAdder ()
return function (a)
return a + withWhat
end
end -- makeAdder
withWhat = 6
f1 = makeAdder ()
withWhat = 7
f2 = makeAdder ()
end -- for
foo ()
print (f1 (2)) --> 9
print (f2 (2)) --> 9
The functions f1 and f2 have 6 and 7 as upvalues to the closures at the time they are created, however they output 9 and 9, which is not what I expect.
I thought f1 and f2 would be closed with their upvalues at the point of their creation, but obviously not. How does 7 become an upvalue for f1, even though it "should" have been 6?
Lua version 5.1.5.
Upvalues of a inner function in lua are copied by address from the enclosing function, resulting in both f1
and f2
's withWhat
upvalues in your second code pointing to the same address.
There are actually two ways to copy:
If an upvalue of the inner function refers to an upvalue of the enclosing function, lua simply copies the pointer.
ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];
This is the case with your second piece of code. As for both makeAdder
and function(a)
, withWhat
is their upvalue, so the withWhat
variable in both functions point to the same address.
If an upvalue of the inner function refers to a local variable of the enclosing function, lua attempts to find the corresponding upvalue from the list of created upvalues in the current stack, it will create a new one if the find fails. (See luaF_findupval)
ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
This is the case with your first piece of code. withWhat
is a variable of the makeAdder
function and an upvalue of the function(a)
function, so lua will create a new upvalue for the function(a)
function to store withWhat
.
If you have another function inside the makeAdder
function that uses the withWhat
variable, lua will reuses the created upvalue. Consider the code:
function foo ()
function makeAdder (withWhat)
return function (a)
local b = a + withWhat
withWhat = withWhat + 10
return b
end, function (a)
return a + withWhat --Will be affected by the above function
end
end
f1, f2 = makeAdder (6)
end
foo ()
print (f1 (2)) --> 8
print (f2 (2)) --> 18