This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.
Programming in Lua | ||
Part II. Tables and Objects Chapter 14. The Environment |
One of the problems with the environment is that it is global. Any modification you do on it affects all parts of your program. For instance, when you install a metatable to control global access, your whole program must follow the guidelines. If you want to use a library that uses global variables without declaring them, you are in bad luck.
Lua 5.0 ameliorates this problem by allowing each function to have its own environment. That may sound strange at first; after all, the goal of a table of global variables is to be global. However, in Section 15.4 we will see that this facility allows several interesting constructions, where global values are still available everywhere.
You can change the environment of a function with the
setfenv
function (set function environment).
It receives the function and the new environment.
Instead of the function itself,
you can also give a number,
meaning the active function at that given stack level.
Number 1 means the current function,
number 2 means the function calling the current function
(which is handy to write auxiliary functions that change
the environment of their caller),
and so on.
A naive first attempt to use setfenv
fails miserably.
The code
a = 1 -- create a global variable -- change current environment to a new empty table setfenv(1, {}) print(a)results in
stdin:5: attempt to call global `print' (a nil value)(You must run that code in a single chunk. If you enter it line by line in interactive mode, each line is a different function and the call to
setfenv
only affects its own line.)
Once you change your environment,
all global accesses will use this new table.
If it is empty, you lost all your global variables,
even _G
.
So, you should first populate it with some useful values,
such as the old environment:
a = 1 -- create a global variable -- change current environment setfenv(1, {_G = _G}) _G.print(a) --> nil _G.print(_G.a) --> 1Now, when you access the "global"
_G
,
its value is the old environment,
wherein you will find the field print
.
You can populate your new environment using inheritance also:
a = 1 local newgt = {} -- create new environment setmetatable(newgt, {__index = _G}) setfenv(1, newgt) -- set it print(a) --> 1In this code, the new environment inherits both
print
and a
from
the old one.
Nevertheless, any assignment goes to the new table.
There is no danger of changing a really global variable by mistake,
although you still can change them through _G
:
-- continuing previous code a = 10 print(a) --> 10 print(_G.a) --> 1 _G.a = 20 print(_G.a) --> 20
When you create a new function, it inherits its environment from the function creating it. Therefore, if a chunk changes its own environment, all functions it defines afterward will share this same environment. This is a useful mechanism for creating namespaces, as we will see in the next chapter.
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |