111. Anonymous functions and the mysterious _G
This post is an attempt to explain a couple of the function mysteries you will see in other people’s code.
Anonymous functions
Maybe you haven’t heard of anonymous functions, or aren’t sure what they are.
Suppose I want a parameter button that the user can use to run a function called AddToA(), that simply adds 1 to a variable called A. I could write my code like this
--in setup parameter.action('Report',AddToA) --and after the end of setup function AddToA() A=A+1 end
So when you press the button, it runs AddToA, which adds 1 to A, as we want.
You could also write the code like this.
--in setup parameter.action('Report',function() A=A+1 end)
Now we are writing a function to add 1 to A inside the parameter itself. And this function doesn’t even have a name. What is happening?
Pointers
I’m going to take this slowly, to try to make it clear, because it helps explain quite a few puzzling things in Lua and Codea.
The secret to understanding it is remembering that variables can only hold numbers (eg 3, -4.12) or text strings (eg “Sunday”). Anything more complicated than numbers or strings – which includes tables, images, functions, vec2 or physics objects – cannot be put into a variable name.
But we know we can label these things using variables, eg tbl={1,2,3}. So, using this example, if a table can’t be put into a variable called tbl, what happens when we write tbl={1,2,3}?
What Codea does is to first create the table and put it somewhere in memory. Then it takes the memory address of the table (which is a number), and puts it in tbl. When you use tbl in your code, Codea sees that it contains a memory address, goes to that place, and figures out that there is a table there.
So the memory address (known as a “pointer”) is like the address of your house, or your phone number, or your car licence plate. It helps identify you.
And this is how you can say tbl={1,2,3}, because tbl will only hold a number which tells Codea where to find the table {1,2,3}.
This helps explain another mystery, which is why you get the following results
--copying numbers a=3 b=a --copy a to b b=5 --change b print(a,b) --prints 3,5 ie change to b doesn't affect a --copying tables a={1,2,3} b=a --copy b to a? b[1]=9 --change something in b print(a[1],b[1]) --prints 9,9 ie the change to b affected a as well
When we copy numbers, we get the result we expect. We can copy a to b, change b, and it doesn’t affect a, because b is completely separate to a.
However, when we copy a table, then change something in the copy, it affects the original table too. This looks crazy, until you realise that what was stored in a was the memory address (pointer) of the table {1,2,3}. So what was copied to b was the memory address. Now a and b both have the same address, which “points” to the table {1,2,3}, so a and b are just different names for the same table, and a change to one of them affects the other.
So how do you copy tables? The hard way, by looping through all the items and copying them one at a time – and only items which are numbers or text will make a separate copy!
Anonymous functions?
You normally write functions like this
function StartPlaying() --code in here end
It may surprise you to know that what Codea does is to create the function, put it in memory – without the name StartPlaying attached – and then it puts the memory address (pointer) of the function in a variable called StartPlaying.
So StartPlaying only holds an address, or pointer, not the actual function. And if I then write Start = StartPlaying, then Start will get a copy of that memory address, and running Start will be the same as running StartPlaying.
I hope you get that. Function names are really just variables holding the memory address of the function. This is why you can also write functions this way, which actually makes it clearer what is happening.
StartPlaying =function() --code in here end
So function names aren’t really needed to create a function. They are just a way of finding the function later, if you need it, like a phone number.
This is why you may sometimes see people write that all functions in Lua/Codea are anonymous. What they mean is that functions don’t have names, only addresses, which you can put into variables that we call function names. But the functions themselves don’t have names.
Anonymous functions
Let’s look at our parameter example again, and explain what is happening.
--in setup parameter.action('Report',function() A=A+1 end)
You can see that instead of giving Codea the name of a function to run when the button is pressed, we’ve included an actual function. Hopefully this doesn’t seem so strange now, because we know functions don’t need names unless you need to call them later.
In this case, we define a temporary function that gets run when the button is pressed, and we’ll never run it from anywhere else in our code, so it doesn’t need a name. If we did want to run it from somewhere else, we would need to give it a name (to hold its address) so we’d know where to find it. But if we only need to run it when the button is pressed, we can define it inside the parameter, without a name. So what the code says is that when the button is pressed, create this little function, run it, and then get rid of it.
Global _G table
Lua has a table named _G that holds all the global variables, including all the functions, both those provided by Lua and Codea, and also any functions you’ve defined. They are held in a “key pair” list, where the key is the name of the variable, function or whatever, and the value is the memory address.
You can list them all by running this code
function allcommands() for n,v in pairs(_G) do print(n,v) end end
Now you know that function names are just variables holding memory addresses, hopefully this kind of list isn’t so puzzling any more.
Modifying functions
You can also modify functions. For example, suppose you are using math.sin, and you get sick of continually having to convert degrees to radians before using it. You could modify math.sin like this
print(math.sin(math.rad(60)) --prints 0.866025 oldsin=math.sin --copy the memory address for math.sin --now create our replacement function --note we still need the original sin function --so we use the copy of its memory address math.sin=function(a) return oldsin(math.rad(a)) end print(math.sin(60)) --prints 0.866025 this is our new, simpler version of math.sin print(oldsin(math.rad(60)) --prints 0.866025 just to check
So we first make a copy of where the original math.sin is stored, because we’ll need it.
Then we redefine math.sin as a function that converts degrees to radians and then uses the original math.sin (whose memory address is now in oldsin).
I wouldn’t do it this way in practice, because I find it confusing to replace a standard function. I would rather create a new name, perhaps Sin, that deals with radians. But I showed this example to illustrate that you can “rename” standard functions, because all you’re doing is swapping memory addresses.
Trackbacks & Pingbacks