lua-users home
lua-l archive

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

On Sat, Jan 9, 2010 at 6:12 PM, Cosmin Apreutesei wrote:
>> I had come to the same conclusion: The policy of how a module is
>> imported should be caller's decision (require), not hard-coded into
>> the module definition (module).
> But doesn't the definition of a module go beyond the idea of an
> importable namespace? Eg., you can have a module that just adds
> methods to a class defined in another module, or a module that changes
> the semantics of the language, like strict.lua, etc.

They may.  These are modules that import with side-effects.  Elsewhere
these are called "pragma" modules [1].  strict.lua is one such
example.  strict.lua also has some issues [2].  Particularly, if your
program has multiple modules that do `require "foo"`, where "foo" is a
pragma module, the side-effects are applied once upon the first
require, whichever one happens to be executed first.  Therefore,
`require "foo"`, as is, is only suitable for pragmas whose
side-effects are to be applied globally (like strict.lua) rather than
applied individually to each caller environment (obtained via
getfenv(2)).  I'd argue that strict should be applied individually,
like in analogues in other languages.  It would be simple to rewrite
strict to behave this way, but you will need to make `require "foo"`
have no side-effects and instead apply side-effects via a separate
call like `require "foo" : import()`.  As a convenience, we can define
a function "import" that wraps the "require" and application of
side-effects so that the client may instead write simply `import
"foo"` or `import("foo", additional_arguments.....)`.  Although you
could bring the import capabilities into the require function, I'm not
arguing for that: rather, there is justification for putting it in a
separate function and keeping the focused purpose of require as is.

Pragma modules are a special case where the policy on side-effects
cannot be entirely delegated to the client (though it may be partly
delegated via e.g. "additional_arguments" above).  For most modules,
the question of where the module should be placed can be delegated to
the client.  The client in turn may delegate that decision to a
generic function like "import" for default behavior, or it may use a
different wrapper around require.  This works best if the
require/module operations don't in themselves impose a policy (as
module and luaL_register do in 5.1).

So, in practice, under my proposal, you'll be able to write your
module like this:

 -- dog.lua
 module "dog"   -- with or without package.clean to privately import _G,
                       --  or alternately something separately like
`import "_G"`
 import "strict"
 import "sounds"
 function bark()
   print "bark"
   sounds.say "bark"

or write it lexically like this:

 -- dog.lua
 local M = {}
 import "checkglobals" --
 local sounds = require "sounds"
 function M.bark()
   print "bark"
   sounds.say "bark"
 return M

and in *either* case, the client can choose to use your module in
either way below (both will work):

  -- test1.lua
  import "dog"
  print(dog.print) --> nil

  -- test2.lua
  local dog = require "dog"
  print( --> nil