lua-users home
lua-l archive

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

I'm surprised I haven't thought of this sooner (nor seen this posted
elsewhere).  Here are two "module" function options that in a simple
way avoid the problem of a module's private namespace being polluted
to its public namespace.

  function package.clean(module)
    local privenv = {_PACKAGE_CLEAN = true}
    setfenv(3, setmetatable(privenv, {__index=_G,
__newindex=function(_,k,v) rawset(privenv,k,v); module[k]=v end}))
  function package.veryclean(module)
    local privenv = {M=module, _PACKAGE_VERYCLEAN = true}
    setfenv(3, setmetatable(privenv, {__index=_G}))


  -- baz.lua
  module(..., package.clean)
  function foo() print 'test' end
  function bar() foo() end

  -- baz.lua  (alternative)
  module(..., package.veryclean)
  function print 'test' end
  function end

  -- test.lua
  require "baz"
  assert(not baz.print) -- globals not exposed (unlike package.seeall) -- ok

This can also be combined with a reimplementation of strict.lua.  See
[1] for further details.

Still, my other complaints about the module function [2] still hold.
Mainly, the module function writes the module's external API to the
shared global environment (via the "luaL_findtable(L,
LUA_GLOBALSINDEX, modname, 1)" call in loadlib.c:ll_module) rather
than to the individual private namespaces of its clients.  Secondly,
the rules for merging the external APIs of two modules with a common
prefix (e.g. foo and, as also defined in luaL_findtable,
break the isolation between foo and bar.  That is, bar is written
inside the foo table, and any other module subsequently doing only
require "foo" automatically gets  These two problems are
easily addressed by importing external APIs of modules only into local
variables of the clients (e.g. local bar = require ""), but
there are also ways to address these by importing them into the
private environment of the clients via an import mechanism like

  import "foo"  -- instead of require "foo"

where "import" is roughly defined as

  function import(name)
    local env = getfenv(2)
    env[name] = require(name)
      -- warning: the above line is similar to luaL_findtable, but
this simple implementation
      -- does not handle dots "." in name.

Unfortunately, we are still left with some possible conflicts when the
module function writes to the shared global environment.  The only way
I see around that is to redefine or avoid the module function.  I
think a suitable resolution along the former lines is to remove the
luaL_findtable call in ll_module and optionally move it into an import
statement like the above.  I'd argue that luaL_findtable is not a
module definition concern but an importation concern.  This also has
the added benefit that clients could define more than one type of
import statement for different types of importation behavior given a
single definition of module.