lua-users home
lua-l archive

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


On Sat, May 19, 2012 at 08:55:37PM -0500, Drake Wilson wrote:
> Quoth Dubiousjim <lists+lua@jimpryor.net>, on 2012-05-19 19:15:56 -0400:
> > I hadn't tried exactly this code, but was simplifying some other tests I
> > had run. Apparently, though, calling print will fail with an error if
> > tostring isn't visible in the active environment (even if print's
> > arguments are all strings). So you need to make newG = { flag = false,
> > tostring = tostring }.
> > 
> > But it still is printing true, contrary to what the documents and my
> > understanding of thread environments seems to imply.
> 
> Global lookups in a function use the function's environment.  Thread
> environments affect _new_ top-level functions created inside that
> thread, but the function you pass to coroutine.wrap has already been
> created from a function (the one for the entire file) in the first
> thread, so setting the thread environment does nothing to it.
> 
> Note that creating new functions inside the function where you do the
> setfenv(0, ...) will also use the outer function's environment, not
> the thread environment.  New top-level functions mainly appear as a
> result of loading code:
> 
>   foo = true
>   local e = { print = print, tostring = tostring, foo = false }
>   coroutine.wrap(function() setfenv(0, e); loadstring('print(foo)')() end)()
> 
> prints "false", since the function returned by loadstring uses the
> environment of the current thread.

Thanks Drake.

I suspected that loadstring (and the like) would behave as you describe. I wasn't
expecting it to be the only way for the new thread global environment to
make a difference. But that's how it looks.

$ lua-5.1 -e 'flag = true; local newg={ flag=false,tostring=tostring,print=print };
coroutine.wrap(function()  setfenv(0, newg); print(flag) end)()'true

$ lua-5.1 -e 'flag = true; local newg={ flag=false,tostring=tostring,print=print };
coroutine.wrap(function()  setfenv(0, newg); (function() print(flag) end)() end)()'
true

$ lua-5.1 -e 'flag = true; local newg={ flag=false,tostring=tostring,print=print };
coroutine.wrap(function()  setfenv(0, newg); coroutine.wrap(function() print(flag) end)() end)()'
true

$ lua-5.1 -e 'flag = true; local newg={ flag=false,tostring=tostring,print=print };
coroutine.wrap(function()  setfenv(0, newg); loadstring("print(flag)")() end)()'
false

I guess my surprise stems from a subtlety about what it means to say a
function is "newly created". In one sense, the second through fourth of
the above examples all have functions being "newly created" after the
setfenv was executed: the code that declares that function and
constructs a function closure is not executed until after the setfenv.

See also:

$ lua-5.1 -e 'flag = true
    local newg={ flag=false,tostring=tostring,print=print }
    local fs = {}
    coroutine.wrap(function()
        setfenv(0, newg)
        for i=1,2 do
            local counter = 0
            fs[i] = function() counter = counter + 1; return counter, flag end
        end
    end)()
    print(fs[1]())
    print(fs[1]())
    print(fs[2]())'
1  true
2  true
1  true

The closures are not created until the loop executes, as is demonstrated
by the independence of their counters. But note that they read flag from
the original thread environment, not from the environment in place when those
closures are created.

In the sense intended in the Lua documentation, though, it's only the
fourth example that shows a function being "newly created" after the
setfenv has been executed. In this sense, once the function declaration
has been parsed into bytecode, the function is for these purposes
counted as "newly created".

-- 
Jim Pryor
dubiousjim@gmail.com