lua-users home
lua-l archive

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


Lorenzo Donati wrote:
>
> I need some help in understanding some Lua code Sergey Rhozenko posted a 
> while ago. They are two version of 'setfenv' to use with upcoming Lua 
> 5.2 in order to replace the missing library function.

I just hope, that people using such code know what they do.
But anyway ...

> The lines that I don't understand are those where debug.upvaluejoin is 
> called. Why is this call needed? Isn't debug.setupvalue enough?

>Version 1: 
>      if name then
>          debug.upvaluejoin(f, up, function() return name end, 1)
>          debug.setupvalue(f, up, t)
>      end

Version 2:
>      if name then
>          debug.upvaluejoin(f, up, function() return t end, 1)
>      end

The 2nd is just an optimization of the first.  Some background:

Upvalues are heap objects that hold values.  They can be shared between
functions.  setupvalue changes the value the upvalue holds.  All functions
sharing that upvalue will see that change:

  local x=1
  function a() return x end
  function b() return x end
  debug.setupvalue(a, 1, 42)
  print(a(), b()) --> 42 42

Here a and b share a single upvalue that is created for x.  If you
modify the value of the upvalue for "a" (via setupvalue), you will
at the same time change the value "b" will see.


On the other hand, upvaluejoin does not modify the value of an upvalue
but replaces the actual upvalue:

  local x=1
  local y=2
  function a() return x end
  function b() return y end
  debug.upvaluejoin(a, 1, b, 1)
  print(a(), b()) --> 2 2
  y=42
  print(a(), b()) --> 42 42

Here, a's first upvalue (x) is replaced by b's first upvalue (y) so
both now reference y.


Back to the setfenv emulation.  The first version

>          debug.upvaluejoin(f, up, function() return name end, 1)
>          debug.setupvalue(f, up, t)

creates a dummy function with one new upvalue (for name) and transfers
this upvalue to f.  In the next step it sets the value of this new
upvalue to t.

The 2nd version

>          debug.upvaluejoin(f, up, function() return t end, 1)

starts the same, but makes sure, the new new upvalue already holds the
correct value (t).  So the second step is no longer needed.


With that background, one can see that there are usually better ways
to handle the environment in Lua 5.2 - especially if you want to
modify the env for a handful of functions:

--8<--
	global = "global"

	do
	    local _ENV = _ENV
	    function demo_setenv(t) _ENV = t end
	    function demo1() print("demo1", global) end
	    function demo2() print("demo2", global) end
	end

	demo1() --> demo1 global
	demo2() --> demo2 global
	demo_setenv{print=print, global="private"}
	demo1() --> demo1 private
	demo2() --> demo2 private
	demo_setenv(_ENV)
	demo1() --> demo1 global
	demo2() --> demo2 global
--8<--

I.E. if you want to change the env for a complete module just add a simple
setenv function to its module table and be done:

	function M.setenv(t) _ENV = t end

Ciao, ET.


PS: I still not sure if a "function environment" is The Right Thing.
I would prefer a simple thread-wide "setglobals" ;-)