lua-users home
lua-l archive

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


Peter & Edgar:
Thank you very much for the useful and detailed explanations.

On 04/05/2011 19.30, E. Toernig wrote:
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 ...


[snip]



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:


Yes, I know. Thanks.

Although I appreciate the improvement in upcoming 5.2 regarding globals management (_ENV etc.) for common uses, i.e. when the old environment is to be changed lexically, I still found setfenv/getfenv extremely useful for a very specific pattern, where the change is to be done dynamically.

Please, consider the following toy code:


---------------------------

-- (simplified) module simulation
-- (just to run the example in a single file)

simulated_require = function()
   local M = {}

   local function DslInterpreter( closure )
      local private_data = {}
      local env = {}
      function env.AddItem( item )
         table.insert( private_data, item )
      end
      function env.PrintResult()
         print( table.concat( private_data, ' ' ) )
      end
      setfenv( closure, env )
      closure()
   end
   M.Interpreter = DslInterpreter

   return M
end


-- client code:

local lib = simulated_require 'mymodule'
local AN_UPVALUE = 'This is a silly DSL!'

lib.Interpreter( function()	-- anonymous closure to emulate scoping

   -- DSL fragment
   AddItem 'hello!'
   AddItem( AN_UPVALUE )
   AddItem 'Hope You Enjoy!'

   PrintResult()	--> hello! This is a silly DSL! Hope You Enjoy!

end)
--------------------------------


Note that this pattern, to my knowledge, cannot be replaced using 5.2 loadin, since AN_UPVALUE is referenced inside the DSL fragment.

Even in the absence of upvalues, loadin incurs the overhead of recompiling the chunk, if that chunk must be run in different environments.

Moreover, this pattern allows resusing the closure with a different environment, if the need arise (e.g., debugging the DSL code with different bindings, etc.). Or even with a different Interpreter.

Well, if 5.2 provided a function like, for example, callin(env,func) analogous to loadin, but which takes a closure instead of a string representing the chunk, I could probably ditch these setfenv/getenv replacements, since I use them only for this specific pattern (and variants). Maybe loadin could also be changed to do the right thing if fed with a closure instead of a string: loadin(env,func)

I doubt that Lua team will introduce such a thing :-), so I must live with my substitutes (thanks to Sergey Rhozenko!).


P.S: I know that I could pass the environment as an argument to the anonymous closure like this:

------------------------------------
lib.Interpreter( function( env )	

   _ENV = env

   -- DSL fragment
   AddItem 'hello!'
   AddItem( AN_UPVALUE )
   AddItem 'Hope You Enjoy!'

   PrintResult()	--> hello! This is a silly DSL! Hope You Enjoy!

end)
------------------------------------


but this is less clean to me: not only the user must remember what to do with the argument to the anon closure, but it adds cluttering boilerplate code. The user of the DSL shouldn't be concerned with that stuff.

Keep in mind that the anon closure could also come from loading a chunk from an external file (yes, there is 'loadin' for that, but then you have to do things in two different ways, depending on whether you load a chunk or use an explicit anon closure).


[snip]


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" ;-)





Cheers

--
Lorenzo