lua-users home
lua-l archive

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


2007/3/24, Graham Wakefield <lists@grahamwakefield.net>:
But I feel like I'm doing a lot of expensive workaround, where a simpler
'environment-less' function would suffice.

You can give a function a proxy environment that will query its caller
environment instead. That's what the 'enslave' function does to foo in
the following example (I picked that name because it makes foo
dependant on its caller) :

function enslave(f)
   local oldenv = getfenv(f)
   setfenv(f, setmetatable({}, {
       __index = function(self, k) return getfenv(3)[k] end;
       __newindex = function(self, k, v) getfenv(3)[k] = v end;
       oldenv = oldenv;
   }))
end

function liberate(f)
   setfenv(f, getmetatable(getfenv(f)).oldenv)
end

function foo()
   b = "c"
   print("foo", a, b)
end

function bar()
   a = "a"
   b = "b"
   print("bar", a, b)
   foo()
   print("bar", a, b)
   print()
end

function baz()
   a = "A"
   b = "B"
   print("baz", a, b)
   foo()
   print("baz", a, b)
   print()
end

-- Set a simple private environment to bar and baz that forwards reads
to globals
setfenv(bar, setmetatable({}, {__index=_G}))
setfenv(baz, setmetatable({}, {__index=_G}))

print "Here foo just use globals"
bar()
baz()

enslave(foo)

print "Now foo reads and writes its caller's environment"
bar()
baz()

liberate(foo)

print "Now foo is back to normal"
bar()
baz()

I think it's elegant, and the performance hit is reasonnable given the
functionnality you ask for. It doesn't change the way you wrote
neither the affected function (foo) nor its callers (bar and baz), and
it's revertable.