lua-users home
lua-l archive

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


On Fri, Aug 6, 2010 at 11:10 PM, Mark Hamburg <mark@grubmah.com> wrote:
> local M = { }
> function M.foo() [...M.baz()...] end
> function M.baz() [...print'baz'...] end
> return M
>
> This hardly seems particularly bizarre and it doesn't rely on tricks with
> environment to change global assignments into exported values.

True, but I was wondering why we can't just express a module in
arguably the most natural way like this:

  -- bar.lua
  local x = 1
  function foo() baz() end
  function baz() print(x) end

There's nothing inherently ambiguous to a compiler here.  There is a
private local variable (x), two global variables "defined" by the
module (foo and baz), and one global variable not defined internally
(print).  By "defined" I mean that there would exist, in 5.1 at least,
a corresponding SETGLOBAL opcode in the chunk.  Defined globals should
get stored and resolved to the public API table of the module (e.g.
SET/GETTABUP M) rather than the base library (e.g. GET/SETTABUP _G),
though on exception this may be overridden by explicitly prefixing
like `_G.foo` .  `print`, being undefined, is intended to be resolved
via GETTABUP _G.  Also, lacking an explicit return value, it should
still be clear that the module's public API table returned by
`require` should default to the table that `foo` and `baz` are stored
in.  Note that there is not necessarily any metatables imposing
runtime overhead or special behavior because the table (M or _G) can
be resolved at compile time, and there is no syntactic overhead either
(e.g. `_ENV =`, `return M`, `M.`).  Yet, there need be no ambiguity.

So, why can't Lua make that same inference?

I think the problem lies partly in 5.1 environments, as well as
locals, not working the way we really want them to here.  As I've
recently suggested [1], unless you're satisfied with the style shown
above by Mark (which IMO is lacking only slightly in syntax not
substance), it seems to me ideal here for there to be two simultaneous
environment tables, not one, to cover the two different types of
globals (M and _G).  For the main chunk and interactive interpreter,
you probably want M == _G, but the searcher function for Lua modules
in package.loaders probably should do like `local M = {}; local f =
loadin(M, _G, source); return function() local result = f(); if result
== nil then result = M end; return result end`.  At least that way
avoids complicating `require`, and it's not required for the C module
searcher function.

Even if I've convinced you on the above proposal for modules defined
as a collection of functions, we still need to consider how well it
works for modules defining other things like classes.  It might
involve

  function foo() baz() end
  function baz() print(x) end

automagically being translated into

  function M:foo() self:baz() end
  function M:baz() _G.print(self.x) end

As seen there are really three environment tables here (_G, M, and
self) and two ways of indexing them ('.' and ':').   I'm not sure
environments offer much help there.

[1] http://lua-users.org/lists/lua-l/2010-08/msg00051.html