Inny wrote:Is checking for setfenv enough to discover 5.1?
No. Quite a few people define
setfenv /
getfenv as compatibility functions (or use modules that provide these.) (Not terribly many, but if you do this, it'll probably break for someone eventually.) (OTOH, if you do stuff that doesn't break if someone defined
setfenv, then you don't need to care. And I think you can assume that if the current interpreter doesn't have
_ENV for environments, then it will have
setfenv.)
If you need to check the version, check
_VERSION ! E.g.
tonumber( _VERSION:sub( -3, -1 ) ) < 5.2
Next, ...
Inny wrote:What am I forgetting? What should I change?
Code: Select all
local module = function(_ENV)
__ = {}
__.reduce = function(fn, array, initial)
local result = initial or 0
for i = 1, #array do
result = fn(result, array[i], i)
end
return result
end
return __
end
local environment = setmetatable({}, {__index = _G})
if setfenv then setfenv(module, environment) end
return module(environment)
Under 5.1 semantics, your function should work but it's rather strange in style. Remember that your file is already a function, so everything that happens in the module body function is essentially defined two functions deep. (That's not a problem, just strange.)
(I haven't touched 5.0 in a looong time, so I hope it's dead enough not to matter.)
Under 5.2 or 5.3 semantics, your function should also work. (And again, strange style.)
What about this?
Code: Select all
local _ENV = setmetatable( { }, { __index = _G } )
if setfenv then setfenv( 1, _ENV ) end
-- or: if tonumber( _VERSION:sub( -3, -1 ) ) < 5.2 then …
-- but setting the environment twice doesn't break anything
-- (For reference: in 5.2+, setfenv is commonly implemented via debug.setupvalue
-- and finding & replacing the right [i]_ENV[/i].)
reduce = function( fn, array, initial ) … end
-- or, if you prefer typing more to prefix all names:
local __ = _ENV
__.reduce = function( fn, array, initial ) … end
-- etc.
return _ENV
Inny wrote:For me, I'd like to know what's a good pattern to use for modules that would not only work in luajit, but 5.2/5.3 (in case I bring this code over to lua.io, or any other node clone). The feature in particular I'm after is simulating _ENV's safety.
For small stuff, I usually use the thing I described above. But I quite liked the idea of the original
module function of Lua. (Except for the part where it always puts something into
_G without caring about what's been there before.)
So for larger stuff, I have my own
Module function that's similar to
module. (You call it and then you're in the module's scope.) The main differences are that it doesn't clutter
_G and that modules have two tables – one module table and one environment table. (I dislike that the
__index trick makes everything visible through the module.) So there's
_M for the actual exposed module and
_MENV for the environment, both linked to each other like
_MENV._M, _M._MENV = _M, _MENV (and the
__index=G on
_MENV doesn't influence
_M). This means I don't need to prefix all internal stuff with
local, so accidentally forgetting that doesn't matter. (Actually, I intentionally
don't prefix stuff with
local, because that makes it easier to reach internals for debugging – instead of looping & poking around with
debug.getlocal to find the variables, I can just say
mymodule._MENV.foo to get the
mymodule's internal
foo.)
If you want to, I can try to extract that from my init files (but it might take a while… and it's probably overkill.)