lua-users home
lua-l archive

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


On Wed, Oct 19, 2011 at 1:32 PM, Roberto Ierusalimschy
<roberto@inf.puc-rio.br> wrote:
>> In terms of "fixing module()", I think Fabio's implementation is right
>> on the money:
>>
>> http://article.gmane.org/gmane.comp.lang.lua.general/84283
>
> What exaclty is it trying to fix?

Mainly, concerns #4 and #5 raised by Mark Hamburg:

> 4. It adds the module to the global namespace. This, in my opinion, is a bad
> thing because it creates hidden dependencies -- i.e., code can use a module
> that it never required simply because some other code required it earlier and
> it became available in the global namespace.
>
> 5. It mucks with the environment to make it "easy" to export functions. But
> then to compensate for this, it offers package.seeall which results in a
> module that reveals a lot of globals in its table which have nothing to do
> with the module -- i.e., it pollutes the API for the module.

...while trying to keep the good intentions of #5 (making it
easy/clean to export functions).

>> It shows that a sane implementation of module() can be done with
>> standard Lua mechanisms. Do any of the anti-module proponents see any
>> problems with that design? Would you hate module() as much as you do
>> now if it behaved like that instead of the way it behaves in Lua 5.1?
>
> Isn't it even more complex/magic than the current implementation?

I think that was in the right direction. Geoff Leyland and Steve
Donovan provided valuable feedback, here's an improved version:

-------- begin loader5.lua ( https://gist.github.com/1298652 )
local lua_libs = {}
for k,v in pairs(package.loaded._G) do
   lua_libs[k] = v
end

local function loader(name, modpath)
  local mod, env = {}, {}
  env.module = function (name)
    mod._NAME = name
    setmetatable(env, { __index = function(t,k) return rawget(mod, k)
or rawget(lua_libs, k) end, __newindex = mod })
    return env
  end
  local fh = assert(io.open(modpath, 'rb'))
  local source = fh:read'*a'
  fh:close()
  local ret = assert(load(source, modpath, 'bt', env))(name)
  return ret or mod
end

-- replace Lua loader
package.searchers[2] = function (name)
  local modpath, msg = package.searchpath(name, package.path)
  if modpath then
    return loader, modpath
  else
    return nil, msg
  end
end
-------- end loader5.lua

In this version, module definitions are not frozen (as per Geoff
Leyland's concern) and no environment overhead is added to modules
that choose not to use module() (as per Steve Donovan's concern).

It may be more complex/magic than the Lua 5.1 implementation of
module() (is this revision as well?), but it does what we all wanted
module() to do (declare a module and allow declaration of public and
private entries through the environment), while giving users
convenience equivalent to that people use package.seeall (another
popular construct) for, without the widely discussed drawbacks of Lua
5.1 module().

> If you really want 'module', wouldn't be simpler to use
>
> _ENV = module(...)
>
> with a simplified version of 'module'?

I tried to come up with a simplified version of module() to see how
close I could get to the behavior of loader5 above. This is as far as
I could get:

-------- begin simplifiedmodule.lua
local lua_libs = {}
for k,v in pairs(package.loaded._G) do
   lua_libs[k] = v
end

function module(name)
  local mod, env = {}, {}
  mod._NAME = name
  setmetatable(env, { __index = function(t,k) return rawget(mod, k) or
rawget(lua_libs, k) end, __newindex = mod })
  return env, mod
end
-------- end simplifiedmodule.lua

-------- begin simple.lua
local mod
_ENV, mod = module("simple")

print(_ENV, mod)

local function bar()
   print("hello")
end

function foo()
   bar()
end

return mod
-------- end simple.lua

-------- begin usesimple.lua
local simple = require("simple")
simple.foo()
print(simple.print)
print(simple._NAME)
-------- end usesimple.lua

Run it with:

lua52 -e'dofile("simplifiedmodule.lua")' usesimple.lua

To my surprise, this causes a segmentation fault in Lua 5.2 beta
(confirmed by Fabio). Commenting the "local mod" in simple.lua:1 makes
the segfault go away but it still doesn't work as I expected (I
probably don't understand _ENV completely).

Still, to get that behavior the simplified module() ends up not being
much simpler than that from loader5, which dispenses with the explicit
_ENV and return.

-- Hisham