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.


15.4 – Using the Global Table

One drawback of all these methods to create packages is that they call for special attention from the programmer. It is all too easy to forget a local in a declaration, for instance. Metamethods in the table of global variables offer some interesting alternative techniques for creating packages. The common part in all these techniques is the use of an exclusive environment for the package. This is easily done: If we change the environment of the package's main chunk, all functions it creates will share this new environment.

The simplest technique does little more than that. Once the package has an exclusive environment, not only all its functions share this table, but also all its global variables go to this table. Therefore, we can declare all public functions as global variables and they will go to a separate table automatically. All the package has to do is to register this table as the package name. The next code fragment illustrates this technique for the complex library:

    local P = {}
    complex = P
    setfenv(1, P)
Now, when we declare function add, it goes to complex.add:
    function add (c1, c2)
      return new(c1.r + c2.r, c1.i + c2.i)
    end
Moreover, we can call other functions from this package without any prefix. For instance, add gets new from its environment, that is, it gets complex.new.

This method offers a good support for packages, with little extra work on the programmer. It needs no prefixes at all. There is no difference between calling an exported and a private function. If the programmer forgets a local, she does not pollute the global namespace; instead, only a private function becomes public. Moreover, we can use it together with the techniques from the previous section for package names:

    local P = {}   -- package
    if _REQUIREDNAME == nil then
      complex = P
    else
      _G[_REQUIREDNAME] = P
    end
    setfenv(1, P)

What is missing, of course, is access to other packages. Once we make the empty table P our environment, we lose access to all previous global variables. There are several solutions to this, each with its pros and cons.

The simplest solution is inheritance, as we saw earlier:

    local P = {}   -- package
    setmetatable(P, {__index = _G})
    setfenv(1, P)
(You must call setmetatable before calling setfenv; can you tell why?) With this construction, the package has direct access to any global identifier, but it pays a small overhead for each access. A funny consequence of this solution is that, conceptually, your package now contains all global variables. For instance, someone using your package may call the standard sine function writing complex.math.sin(x). (Perl's package system has this peculiarity, too.)

Another quick method of accessing other packages is to declare a local that holds the old environment:

    local P = {}
    pack = P
    local _G = _G
    setfenv(1, P)
Now you must prefix any access to external names with _G., but you get faster access, because there is no metamethod involved. Unlike inheritance, this method gives you write access to the old environment; whether this is good or bad is debatable, but sometimes you may need this flexibility.

A more disciplined approach is to declare as locals only the functions you need, or at most the packages you need:

    local P = {}
    pack = P
    
    -- Import Section:
    -- declare everything this package needs from outside
    local sqrt = math.sqrt
    local io = io
    
    -- no more external access after this point
    setfenv(1, P)
This technique demands more work, but it documents your package dependencies better. It also results in faster code than the previous schemes.