lua-users home
lua-l archive

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


On Fri, Feb 22, 2013 at 1:49 AM, steve donovan
<steve.j.donovan@gmail.com> wrote:
> On Thu, Feb 21, 2013 at 8:50 PM, James Graves
> <james.c.graves.jr@gmail.com> wrote:
>> import { _ENV,
>>     baz = "foo.bar",
>>     math_iz_awezome = "math",
>>     "os",
>> }

James: so this is kind of multi-require, where the first value is the
environment or global table for each module to use? The key is the
local to copy into and I take it that the numbered keys (os in your
example) would be copied into _ENV, such that `os.clock` becomes
`_ENV.clock` or just `clock`.

Do I have that correct?

> But now, how about the 'leaky module' problem, which is why
> module(...,package.seeall) got such a bad rap?  Your new module will
> be displaying its underwear, e.g. we could access os, which might very
> well be a no-no in another context.
>
> One solution would be to use a shadow table - you put everything you
> want into _ENV, but keep an _actual_ module table; _ENV will have a
> __newindex which will write any new functions into the module table.
> One just has to remember to return that module, not _ENV itself.

You and I discussed this off-list and I'm still not following you. In
order for what you're contemplating as an issue to make sense, one
would presume that the module writer is exporting _ENV, instead of a
table with their module's properties and methods. So the module writer
does something like:
---
--env_not_safe.lua
_ENV = require'pl.import_into'(_ENV)
m = {my_module = {}}
local secret = {nobody_will_see_this = {}}
--awesome module stuff...
return _ENV
----

Now `m` is exported along with the entire environment, except the
locals, which die at the end of scope. The issue that you're
identifying is that `pl` is now available, inadvertently, to the
consumer of the module.

Steve, have I correctly stated your concern? I ask, because this is
the behavior that would bring back memories of `package.seeall` and I
don't see it as an issue anymore, in 5.2

As the module author, it is pretty obvious to me that this is what is
going to happen and what I intend to have happen. What's more,
everything, not just Penlight, is exported AND it's exported into the
return value of the module, not into the user's global environment.

The behavior that everyone usually wants is the result of the more
natural, never have to even think about it, construct that goes like
so:

--safemodule
_ENV = _G
_ENV = require'pl.import_into'(_ENV)
assert(class and stringx and print and load and
	require and coroutine and ipairs and next)
local m = {my_module = {}}
return m
----

The user need not know anything about _ENV and neither does the
package writer. They could just as easily have written:

--safemodule2
pl = require'pl.import_into'({})
assert(pl.class and pl.stringx and print ) ---etc.
local m = {my_module = {}}
return m

Either way, the user of the module gets the table defined in `m` with
the single method `my_module`. They don't get pl in either sefemodule2
or in safemodule.

Exporting _ENV and importing it from "require" buys you many (maybe
all?) of the tricks that James is after, although in a different way
and so it's not a direct replacement:

--env_export.lua
_ENV = {print = print,  require = require}
_ENV = require'pl.import_into'(_ENV)
return _ENV
--
this module, when loaded like so:

_ENV = require'env_export'

makes the environment contain only print, require and all of the
dynamically loaded Penlight submodules. Such that:

assert(tablex and stringx and print and require)

passes, but

assert(stringx.load or tablex.ipairs or ipairs or next) --etc

would fail. Which is what you want, most likely.

To me, "exporting _ENV trick" is crazy awesome and is only one example
of why I really admire the work and artistry of the changes in Lua
5.2. (not that 5.1 sucks, but why use it, if you don't need to?)

The only way I can see a concern about the return of the unintended
side-effects of `package.seeall` is if there was some kind of magic
property at play in _ENV It is just the table handed as the first
upvalue to every new block's scope, inheriting from whatever the
parent scope's _ENV was pointing to. By default this is _G, but the
fun is in changing it! :)

In the case of loaded blocks, just as in 5.1 and before, they load
into Lua's global `_G` environment, unless they are explicitly told
not to (not possible in the case of `require`, but who said _G was
magic?).

-Andrew