So first I’m going to assume you know a little bit about functions and anonymous functions. If you don’t, here’s a little crash course:
In Lua, a function can be stored in a variable. In fact, function names are technically just variable names. For example:
function A() print(":)") end B = A A() B() A = 2 print(A) B()
:) :) 2 :)
In fact, the syntax you most commonly see to define a function
function ThisIsTheFunctionName(a,b,c) return a*b*c end
Is really just shorthand for
ThisIsTheFunctionName = function(a,b,c) return a*b*c end
In fact, you can use a function anywhere you would put a variable: As an argument to another function (which is how events work), you can pass them around, redefine them, and even return them!
What’s a closure
Closures hail from the land of functional programming: descendant from hardcore mathematics. As such, it’s easiest to introduce them with fairly mathematical examples. Let’s take a look at some lua code.
function AddX(x) return function(y) return x + y end end
Returning functions? That’s a little strange. Well fear not, in Lua it’s perfectly acceptable! When you call the first function, it will return another thing that you can call, just like the first. Maybe some syntax will clear it up
print(AddX(3)(4)) print(AddX(5)(6)) print(AddX(8)(9)) --[[ output: 7 11 17 ]]
So here’s where things get interesting. Let’s say we store the result of AddX into some variables:
Add3 = AddX(3) print(Add3(5)) Add4 = AddX(4) print(Add4(5)) Add5 = AddX(5) print(Add5(5))
8 9 10
Here’s where things get interesting though. Every time you call AddX the x parameter gets overwritten right? That would be expected because there’s only one variable called “x”. However, after calling Add5 Add3(5) is still 8. This is where I define exactly what a closure is:
A closure is a variable inside a function that is defined outside the function. When the function is defined, the value of that variable is closed over, meaning that external changes to the variable are now ignored. This applies to any variable which is local. The grouping of closed over values in a particular context is called a closure. The technical definition is a record storing a function together with an environment.
This is actually pretty intuitive. You’ve probably used closures before without even realizing it. Let’s take the following spawning system:
game.Players.PlayerAdded:Connect( function(Player) Player.CharacterAdded:Connect( function(Character) Character.Humanoid.Died:Connect( function() wait(5) --The variable "Player" is closed over, as it is a parameter to function sent to the PlayerAdded event Player:LoadCharacter() end ) end ) Player:LoadCharacter() end )
If a closure was not created then the “Player” variable would change every time a new player joined the game, and when your character died, you would not respawn, instead the most recent player to join would respawn.
Again, you’ve probably been doing these for ages, but you might not have been able to pin down a name for them.
This is hardly the only use for closures. If you guys like this tutorial I will go into more in depth usage with them, including how they can be used to replace objects.