Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

local variable cannot be seen in a closure across the file?

Tags:

lua

Suppose I have the following two Lua files:

In a.lua:

local x = 5
f = dofile'b.lua'
f()

In b.lua:

local fun = function()
  print(x)
end
return fun

Then if I run luajit a.lua in shell it prints nil since x cannot be seen in the function defined in b.lua. The expected printing should be 5. However if I put everything in a single file then it's exactly what I want:

In aa.lua:

local x = 5
local f = function()
  print(x)
end
f()

Run luajit aa.lua it prints 5.

So why x cannot be seen in the first case?

like image 359
peng sun Avatar asked Oct 24 '15 23:10

peng sun


People also ask

What happens to a local variable when it goes out of scope?

These variables are referred to as local because they exist (and are visible) only during the lifetime of the method. They are said to have local scope. When the method ends, the variable goes out of scope and is destroyed. C# divides the world of types into value types and reference types.

Does local variable get destroyed?

Local Variable: These variables are declared within a method but do not get any default value. They are usually created when we enter a method or constructor and are destroyed after exiting the block or when the call returns from the method.

What does closure do by preventing the use of global values?

Closures can avoid the use of global values and provides some form of data hiding. It can also provide an object oriented solution to the problem.

How do you declare a local variable in Python?

In Python or any other programming languages, the definition of local variables remains the same, which is “A variable declared inside the function is called local function”. We can access a local variable inside but not outside the function.


2 Answers

As their name suggests, local variables are local to the chunk.

dofile() loads the chunk from another file. Since it's another chunk, it makes sense that the local variable x in the first chunk isn't seen by it.

like image 60
Yu Hao Avatar answered Nov 15 '22 08:11

Yu Hao


I agree that it is somewhat unintuitive that this doesn't work.

You'd like to say, at any point in the code there is a clear set of variables that are 'visible' -- some may be local, some may be global, but there is some map that the interpreter can use to resolve names of either kind.

When you load a chunk using dofile, then it can see whatever global variables currently exist, but apparently it can't see any local variables. We know that 'dofile' is not like C/C++ inclusion macros, which would give exactly the behavior you describe for local variables, but still you might reasonably expect that this part of it would work the same.

Ultimately there's no answer but "that's just not how they specified the language". The only satisfying answer is probably along the lines 'because otherwise it would cause non-obvious problem X' or 'because then use-case Y would go slower'.

I think the best answer is that, if all names were dynamically rebound according to the scope in which they are loaded when you use loadfile / dofile, that would inhibit a lot of optimization and such when compiling chunks into bytecode. In the lua system, name resolution works like 'either it is local in this scope, and then it binds to that (known) object, or, it is a lookup in the (unique) global table.' This system is pretty simple, there are only a few options and not a lot of room for complexity.

I don't think that running byte code even keeps track of the names of local variables, it discards them after the chunk is compiled. They would have to undo that optimization if they wanted to allow dynamic name resolution at chunk loading time like you suggest.


If your question is not really why but how can I make it work anyways, then one way you can do it is, in the host script, put any local variables that you want to be visible in the environment of the script that is called. When you do this you need to split dofile into a few calls. It's slightly different in lua 5.1 vs lua 5.2.

In lua 5.1:
In a.lua:

local shared = { x = 5 }
temp = loadfile('b.lua')
setfenv(temp, shared)
f = temp()
f()

In lua 5.2:
In a.lua:

local shared = { x = 5 }
temp = loadfile('b.lua', 't', shared)
f = temp()
f()
like image 39
Chris Beck Avatar answered Nov 15 '22 10:11

Chris Beck