5.5 Closures

If a function loses access to the data it needed from its enclosing scope, that would kind of limit the usability of first class functions and the ability to define and use them anywhere.

In order to fully realize their usefulness, behavior has to be implemented so that the lexically scoped data outlive its enclosing scope.

This concept is known as closure. The lexically scoped data enclosed by the nested function is moved to a different region of memory that persists after the stack has been returned, an upvalue.

Lua
local function getFunction()
	local counter = 0

	local function innerFunction()
		counter = counter + 1
		print(counter)
	end

	return innerFunction
end

local foo = getFunction()

foo() --Output: 1
foo() --Output: 2
foo() --Output: 3
print(counter) --Output: nil

This is done automatically by Lua. And now each time that function is called, it references the same upvalue.

Because closures create their own upvalue, each time the enclosing function is called, a new upvalue is created. This makes it possible to define several of the same function that references its own data.

Lua
local function getFunction()
	local counter = 0

	local function innerFunction()
		counter = counter + 1
		print(counter)
	end

	return innerFunction
end

local foo = getFunction()
local bar = getFunction()

foo() --Output: 1
foo() --Output: 2
bar() --Output: 1
foo() --Output: 3
foo() --Output: 4
bar() --Output: 2