lua-users home
lua-l archive

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


Those are some good points:

On Mon, Oct 17, 2011 at 4:44 PM, Hisham <hisham.hm@gmail.com> wrote:
> [In Lua 5.0] require() didn't enforce the
> return of the module, so some packages would just add to the global
> namespace, some would return a table, etc.

Perl, for all its ability for "keeping easy things easy" has a
slightly annoying requirement that a module must explicitly return a
true value at the end of the file. [1]  However, if you omit the
return, then the function loading the module simply fails.  So, there
is not a risk that some users will omit the return value.  The lesson
I think is that the return can be enforced.

[1] http://stackoverflow.com/questions/5293246/why-the-1-at-the-end-of-each-perl-package

> The module() function gave us an easy answer for the question "how do
> I write a module" that mirror other languages that offer constructs for modularity.
> [...] If module() is broken beyond repair and there is no clean way to add a
> library function to do that, I'd dare to say that this is something
> important enough that a language construct would be justifiable

OTOH, there are multiple ways to use this function: The somewhat
canonical "easy way" involves package.seeall, and maybe we should all
just accept that, in the way in another life I just accept Perl 5.
Josh's principle I think is important though: "[it's about] making it
easier to do the right thing. With module it's easy to get things
wrong".

> But it's really nice to be able to open a source file and read something like " module('foo.bar.baz') "
> on top, which gives a clear indication of how this module should be loaded.

Yes.  Most recently, in packaging a certain project, it wasn't clear
to me where the modules should be installed or how they should be
loaded with require (though, thankfully, the author addressed these
concerns).  The "module(...)" trick doesn't help here, which perhaps
should be discouraged, or (as Mark just noted) at least accompanied
with good comments.

> requiring users to learn about _ENV in order to write a reusable
> module is, in my opinion, worse.

True, I don't plan to use _ENV for module definition in 5.2.

> that it won't pollute the global namespace,
> that you can assign the return of require() to a local.
> It's important to have
> those guarantees when writing code with reusable components.

IMO, it's a question of timing.  If the module is popular enough,
someone will eventually complain to the author to fix that.  Even
earlier, someone could complain to the author when the module was
submitted to LuaRocks.  Even earlier, Lua itself could complain (at
runtime or compile time).

~~~

Now, if one wanted to "fix" the module function, I think a solution
roughly along the following lines might be acceptable to all parties.
Modules could be written like this:

  -- foo.lua, to be loaded with foo = require 'loadmodule' 'foo'
  module "foo"
  local baz = require 'loadmodule' 'baz'
  function M.bar()
    return math.sqrt(16) + baz.qux()
  end

and the pluming might be defined more-or-less like this:

  -- loadmodule.lua
  return function(name)
    local M = {}
    local called
    local function module(name)
      M._NAME = name
      assert(not called, 'module function used twice')
      called = true
      return M
    end
    local env = setmetatable({M=M, module=module}, {__index = _G})
    local modpath = package.searchpath(name, package.path)
    local fh = io.open(modpath, 'rb')
    if not fh then error('could not open file', 2) end
    local source = fh:read'*a'
    fh:close()
    assert(load(source, modpath, 'bt', env))()
    if not called then
      error('"module" call expected in module.  Is this really a module?', 2)
    end
    return M
  end