lua-users home
lua-l archive

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


On Fri, Oct 21, 2011 at 2:49 PM, Sam Roberts <vieuxtech@gmail.com> wrote:
> Everybody who _REALLY_ thinks having module names
> [not?] be global, I'd say you should be lobbying for all the
> standard modules to be removed from the global namespace.
> I'd be vehemently opposed, of course, but at
> least you'd be consistent!

That's a good question.  The way I see it, we have this table called
"_G".  It contains the standard library, which are functions that
almost all programs frequently use.  Some rare programs might not use
any of it.  Many other programs might not use parts like debug.  Some
sandboxed programs may an alternative table containing a subset of _G
with some wrapped or reimplemented functions.

If you like, you can access this _G in the standard way via require:

  local _G = require '_G'

To be pedantic, require itself is inside _G, so you should really
write `_G.require '_G'`, but that's pointless, and you minus well
write `local _G = _G`, which I *have* seen people do, including PiL's
module chapter.  So, we can now do things like

  local _G = _G
  local M = {}
  M._NAME = 'baz'
  local QX = _G.require 'foo.bar.qux'
  function M.print_circle_radius(area)
    _G.print(_G.math.sqrt(area / _G.math.pi), QX.buzz())
  end
  return M

which "technically" there's nothing wrong with.  The module does
exactly what it's supposed to do.  You may bulk at the syntax, but
it's just a normalized/proper form, and an editor or preprocessor
could make it nicer to view and manipulate (as with XML editors).

Also, no, we don't need to do require'math'.  _G has a subtable
containing that already.  I may in fact want to write my own version
of _G that has math functions at the top level.  I'll just then do
`local _G = require '_Gdave'`, which let's me access _G.sqrt directly,
an alias _G.square_root, or whatever.

You can replace the _G variable with _ENV in the above code, which
will remain equivalent.  Moreover, Lua 5.2.0-beta has a syntactic
sugar that allows you to omit _ENV.  Therefore, the following code is
exactly equivalent and generates the same bytecodes:

  local _ENV = _G
  local M = {}
  M._NAME = 'baz'
  local QX = require 'foo.bar.qux'
  function M.print_circle_radius(area)
    print(math.sqrt(area / math.pi), QX.buzz())
  end
  return M

Moreover, the only truly "global" variable in the above code is the
first access to "_G" in "local _ENV = _G", but the module loader does
this for you, so you can omit that line.  Therefore, it can be argued
that the above code doesn't use any global variables.  This code is
also the traditional "M" table form of module definition.

Now, you may argue that we should have set _ENV to M rather than _G.
There's nothing wrong with that.  C++ without "using std;" is kind-of
like that.  However, I think we use _G functions much more often than
M functions, so that's my choice.  There's likewise nothing wrong with
setting _ENV to QX, but that's probably even less useful.  (You may
notice that my convention is to capitalize namespaces.)  Some people
prefer to marge _G and M into the same namespace, which can be
trickier, and there's unclean (package.seeall) and clean ways to do
that, often with a little metatable overhead.  That is (or should be)
just an implementation decision.  I've made my own choice on that.

Now, I could stuff QX into _G or M, but why would I want to mutate a
module...  If I have a frequent need for QX, I could just put it into
my '_Gdave' module mentioned earlier.