lua-users home
lua-l archive

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


On Jan 13, 2010, at 3:00 PM, Edgar Toernig wrote:

> Could this possibly resolved with a dynamic "in-do-end"?  That is,
> "in env do <code> end" installs env as the new table of globals for
> <code> and everything <code> calls.  When end is reached (or an
> error is raised) the previous table is restored.  These in-do-end
> should nest (I admit, it's not easy to get nesting fast).
> No function environment any more, only a global environment.  If
> functions want to protect against sandboxing, they have to deal
> with that by themselves.
> 
> I have no idea whether all details of a dynamic in-do-end can be
> defined reasonably (abort search, post write, direct access to
> upper levels, thread creation, a=a+1, etc).  But I would prefer
> this direction.  Just my opinion - I know there are people who
> disagree.  And there are even people who use Boost ;-)

 While there are certainly cases where dynamic scoping has seemed appealing, I think it's in-do-end that seems to bring it to the forefront since it provides linguistic (rather than just library) support for changing the environment but that almost immediately brings up the issue that any functions called within in-do-end don't get the new environment. (Evidence: Note the number of people who tried to write a setfenv environment as "in env do f() end".)

So, what's the concrete proposal for dynamic scoping? I'm stating this in order to have a concrete object of discussion rather than specifically as advocacy (though I'm at least intrigued by the mental exercise).

1. Functions don't have environments.

2. Free variables are looked up in the current environment.

3. The current environment is maintained in the call frame.

4. Calling a function sets the environment for the new call frame equal to the environment for the calling frame.

5. "in env do < code > end" changes the environment for the current call frame. It changes it back after the end. Thus, it becomes syntactic sugar for:

	local (saved_environment) = vm_get_current_environment()
	vm_set_current_environment( env )
	do < code > end
	vm_set_current_environment( (saved_environment) )

6. Note that stack unwinds through returns or errors automatically do the right thing by virtue of unwinding the call stack.

Evaluation:

This clearly handles the sandboxing use:

	in sandbox do
		sandboxed_function()
	end

It is hostile to isolation, but isolation can be achieved by loading any values one cares about into local variables. For that matter, if there is a way to get the current environment, you can save that in a local variable during the first execution of the chunk.

Is the language still Lua? Are the various flavors of Lisp with different lookup rules still Lisp? The one thing I probably would like that would be un-Lua-like would be a naming convention for free variables. For example, I might want to write:

	function setPhotoField( photoID, fieldName, value )
		local photoData = _PhotoStore_:read( photoID )
		photoData[ fieldName ] = value
		_PhotoStore_:write( photoID, photoData )
	end

_PhotoStore_ is defined dynamically so that I don't have to pass it everywhere. At the same time, I've got better control over it than I would as a conventionally handled global variable since somewhere up the chain I presumably have to write:

	in _G:pushenv{ _PhotoStore_ = aPhotoStore } do
		-- do work with aPhotoStore
	end

But naming could just be a convention enforced by a project specific linter and wouldn't need to be part of the language.

Do I feel Lua should shift to dynamic scoping? It's a radical change and it almost certainly breaks a fair amount of code. On the other hand, a lot of code probably also survives and a number of things become simpler and potentially clearer.

Mark

P.S. I did not include in this discussion the notion of assigning to variables as part of the in-do-end construct. I think that's an orthogonal issue to how scopes are handled.