Posted 25 June 2013 - 03:52 PM
One of Lua's most powerful features is setting function environments. Environments are tables that hold the global values that a function references.
The function a sets the global variable c to 3. Also, the declaration of the a function sets the global a to that function. The result of this program is printing the number 3.
So what table is holding these a and c keys? That's known as the environment! For programs run from the shell, the environment is a shared one across all programs run by the shell. So if you set a global in one program, the environment holds onto it, and any other program can access it.
But how do we set our own environments? There's a nice little function called setfenv.
a gets its environment set to env, so when c is defined globally, it is stored in env. So now we can keep track of the globals declared by a function. But this leaves us with a problem. Now a can't access global functions. This is easily solved with metatables
And now we can get pretty clever with setting and getting variables. If we wanted to, we could create a setter/getter system that could be very useful.
In writing that, I almost made a mistake that should be noted. I accidentally called the local _width variable width. This would have been a problem. f() would be referencing a local variable, instead of setting a global, and the environment __newindex metamethod never would have been called.
So hopefully this shed some light on the idea of globals, that seems so mysterious to so many people. Happy coding!
function a()
c = 3
end
a()
print(c)
The function a sets the global variable c to 3. Also, the declaration of the a function sets the global a to that function. The result of this program is printing the number 3.
So what table is holding these a and c keys? That's known as the environment! For programs run from the shell, the environment is a shared one across all programs run by the shell. So if you set a global in one program, the environment holds onto it, and any other program can access it.
But how do we set our own environments? There's a nice little function called setfenv.
local function a()
c = 4
end
local env = {}
setfenv(a, env)
a()
print(env.c)
a gets its environment set to env, so when c is defined globally, it is stored in env. So now we can keep track of the globals declared by a function. But this leaves us with a problem. Now a can't access global functions. This is easily solved with metatables
local function a()
print("test")
end
local env = setmetatable({}, {__index=_G})
setfenv(a, env)
a()
And now we can get pretty clever with setting and getting variables. If we wanted to, we could create a setter/getter system that could be very useful.
local setters = {}
local _width
function setters.setWidth(w)
-- We can not only set the local _width value, but also do some logic
_width = w
redrawScreen()
end
local env = setmetatable({}, {__index = getfenv(), __newindex=function(t,k,v) -- FYI, getfenv has various functions
-- one of which is to get the current environment
local setter = setters["set" .. k:sub(1,1):upper .. k:sub(2)]
if type(setter) == "function" then
setter(v)
end
end})
local function f()
width = 5 -- now setting this width key automatically calls a redraw routine!
end
setfenv(f, env)
f()
In writing that, I almost made a mistake that should be noted. I accidentally called the local _width variable width. This would have been a problem. f() would be referencing a local variable, instead of setting a global, and the environment __newindex metamethod never would have been called.
So hopefully this shed some light on the idea of globals, that seems so mysterious to so many people. Happy coding!