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 15. Packages |
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) endMoreover, 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.
Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. |