lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Mon, Aug 2, 2010 at 12:55 PM, Jerome Vuarand
<jerome.vuarand@gmail.com> wrote:
> [code approximately as follows]
> local _M = {}; local _ENV = setmetatable({},{__index=_ENV, __newindex=_M}); function foo() _M.bar() end; function bar() print'!' end; return _M

I haven't found it advantageous to merge the _G table and a module's
public table into the same top-level global namespace because Lua
doesn't perfectly support the concept of there being *two*
simultaneous environment tables (e.g. _ENV1 and _ENV2 [1] or
package.clean [2]).  Lua does, however, support one environment table
sharing the top-level namespace with local variables, which leads to
some variant of these four approaches:

  local M = {}; function M.foo() M.bar() end; function M.bar()
print'!' end; return M
  local function bar() print '!' end; local function foo() bar() end;
return {foo=foo, bar=bar}
  local _G = _ENV; _ENV={}; function foo() bar() end; function bar()
_G.print'!' end; return _ENV
  local print=print; _ENV={}; function foo() bar() end; function bar()
print'!' end; return _ENV

IMO, the these are fine semantically, with the first one being the
simplest and the second being the most efficient.  However, if you'd
like, you could devise ways for Lua to shorten them.  For example, let
`require` build a private environment {__index = _G, M = {}}, to avoid
the `local M = {}; ..... return M` boilerplate or make _M be a macro
that auto-expands to a table of all local functions except "private"
functions prefixed by '_'.

But here's another, IMO practical, idea along the lines of supporting
"two simultaneous environment tables":

  _ENVW,  = {}
  _ENV = _G  -- this line actually can be omitted but is included for clarity
  function foo() bar() end
  function bar() print'!' end
  return _ENVW   -- Note: rename _ENVW to something friendlier like
_PUBLIC if you want

What this would mean is that any global variable ever written to
within the current lexical scope (i.e. foo and bar) would be resolved
to the _ENVW table for both reads and writes, and all other global
variables that are only read from would be resolved to _ENV.  So, the
above becomes bytecode-equivalent to

  local _ENVW = {}
  local _ENV = _G  -- this line actually can be omitted but is
included for clarity
  function _ENVW.foo() _ENVW.bar() end
  function _ENVW.bar() _ENV.print'!' end
  return _ENVW

and also has basically the same bytecode as my current preferred method:

  local M = {}; function M.foo() M.bar() end; function M.bar()
print'!' end; return M

[1] http://lua-users.org/lists/lua-l/2010-07/msg00262.html
[2] http://lua-users.org/wiki/ModuleDefinition