Lua: check local or global variable - variables

Is there a way to check if variable is global or local? I mean to do it using programming facilities, not by reading a code?
For example
print(type(a))=>"number",
print(checklocal(a))=>true

Variables are local in certain scope. Yes, you can access the local variables of any active function by calling debug.getlocal(StackLevel, Index), it returns two values:
Name of the variable
Value of the variable
-- Example
local isGlobal = 99
function testFunc(var)
local v1, v2, v3 = 1, 2, 3
local a = 1
while true do
local name, value = debug.getlocal(1, a)
if not name then break end
print(name, value)
a = a + 1
end
end
testFunc('xyz')
-- Result
-- var xyz
-- v1 1
-- v2 2
-- v3 3
-- a 5
local isGlobal = 99 is outside of the scope of our testFunc() hence getlocal will not print it, whereas all other variables within scope of our testFunc are printed with their values.

The locality of any given variable can be always determined from the source, so if you program in pure Lua, I can't see any purpose of this. I suppose it might be useful in cases where you do some preprocessing perhaps, so here is one way of doing it, without having to depend on the name:
local variable
print(debug.getupvalue(function()variable()end, 1) ~= "_ENV")
function()variable()end will, for a local variable, capture it as an upvalue (debug.getupvalue will return "variable"). If variable gets resolved to a global variable instead, the function has to store the global environment, _ENV, as an upvalue instead (which gets indexed with the name of the variable). There is no need to call variable in the function, this is just the shortest way to "use" it. If variable is _ENV, it always prints false (even though _ENV is always local).
Another option, which works correctly for _ENV:
local variable
print(debug.getupvalue(function()_ENV(variable)end, 2) ~= nil)
function()_ENV(variable)end always captures _ENV, and variable as well, if it is local, so checking the second index determines whether the variable was local or not.
This works from Lua 5.2 onwards.
For a solution without using the debug library, you can temporarily set the __newindex metamethod on _ENV, assign some values around, and check if the metamethod gets called.

Related

Evaluate a local variable inside a Julia macro

I'm writing a Julia macro that takes an expression and serializes it, so that I can run it somewhere else. The macro therefore takes the expression and replaces all symbols with variables. The expression is then serialized and evaluated somewhere else.
My problem is related to evaluating variables that are not in the global scope. I.e. the following works fine, as a is defined in the global scope:
macro myprintf(ex)
print(eval(ex))
end
# works
a = 2
#myprintf a
This throws an error as the macro doesn't see a, which is defined in the local scope of the loop (run in a new session):
macro myprintf(ex)
print(eval(ex))
end
# UndefVarError: a not defined
for j=1:3
a = 2
#myprintf a
end
Is there any way I can access a inside the macro if it is defined in a local scope such as a loop? I'm aware that I'm not necessarily using macros as intended as I am calling eval on the expression inside the macro definition. The overall idea is that I want to serialize the expression that's passed to the macro and evaluate it somewhere else later (e.g. in a different Julia session).
eval only works in global scope. But then I don't see how much use there is in replacing the variables by evaluated literals, resulting in an expression of literals.
Anyway, a different approach to the original problem could be mimick what R does: constructing thunks out of an expression plus its environment at the place where it was called. To recreate that in Julia, you'd have to
Figure out the free variables in the expression
Create an environment data structure of their values
Wrap the original expression in a closure such that it takes the local variables from the environment
Put everything into a new object
Example:
for j=1:3
a = 2
#saveexpr a + 2
end
should expand to something like
for j=1:3
a = 2
SerializedExpr(
(; a),
function (env)
let (a,) = env
a + 2
end
end,
:(a + 2))
end
If saving the closure is not feasible in your use case, I guess you have to either implement your own evaluator, or use something like JuliaInterpreter.jl.

Lua closure reference to variable fails if declared in the same statement declaring the variable

This is a question about closures in Lua. I stumbled across a problem (and workaround) while trying to make an object registrar object as follows:
tracker = {
objList = {},
myRegister = function(self, obj)
table.insert(self.objList, obj)
return "hello"
end,
myInit = function(self)
local i, obj
for i, obj in ipairs(self.objList) do
obj:init()
end
end,
}
-- Note: As written, this does *not* work.
-- It *will* work if I separate the line into two parts as follows:
-- local myvar
-- myvar = tracker:myRegister({
local myvar = tracker:myRegister({
init = function(self)
-- This will generate an error complaining that myvar
-- is a global variable with a "nil" value
print("myvar = " .. myvar)
end,
})
tracker:myInit()
It seems that if I declare the local variable, "myvar", in the same statement which creates a closure, then the local variable is not accessible from the closure. If, however, I merely assign to an already-existing local variable, then that variable is accessible from the closure.
Obviously, I know how to fix this: Just declare myvar separately.
My question, however, is this: Why is this necessary? Is this by design, or is it a bug in the compiler and/or virtual machine? If it's by design, where is it documented? I'm particularly interested in whether this behavior has other implications, and what those might be, and I'm hoping that the documentation (again assuming this is the intended behavior) will shed some light on this.
Yes, this is the intended behavior.
It is documented in the Lua manual §3.5 – Visibility Rules
This feature allows you to write the following code:
print"Beginning of log"
do
local print =
function(...)
print(os.date"%T", ...) -- Here you're invoking global "print"
end
-- inside this do-end block "print" is automatically adding current time
print"Some event"
print"Another event"
end
print"End of log"
In other words, while the shadowing object is being created, the original object is still accessible.
This is quite useful.

How to declare a variable without initting it to nil in Lua?

I've found it very useful to do something like:
if not variable then
variable = value
end
Of course, I'd usually rather that variable was local, but I can't declare it local in my if, or it won't be accessible.
So sometimes I do:
local variable
if not variable then
variable = value
end
The problem is that when I iterate over this code, the variable declaration sets the variable equal to nil. If I can live with a global value (I can), I can get around it by just not declaring the variable outside of the if block.
But isn't there some way that I can both have my local value and let it keep its value?
First off, the way or is defined in Lua gives you a nice idiom to avoid the if altogether:
variable = variable or value
If variable is nil, or will evaluate to its second operand. Of course, this will only work, if false is not a valid value for variable (because false and nil are both "false" as far as or is concerned).
However, you still have the problem that you need to declare the variable somewhere. I suppose your problem is that in the case of a global loop you think you have to do either:
while condition do
variable = variable or value
process(variable)
end
(which would make variable global) or
while condition do
local variable
variable = variable or value
process(variable)
end
which is pointless because local limits the scope to one iteration and reinitializes variable as `nil.
What you can do though is create another block that limits the scope of local variables but does nothing else:
do
local variable
while condition do
variable = variable or value
process(variable)
end
end
There's a simple way to test the scope of locals inside loops:
local a = "this is external a"
for x = 1, 3 do
print(a)
local a = "THIS IS INTERNAL a"
print(a)
end
If you run this you'll get:
this is external a
THIS IS INTERNAL a
this is external a
THIS IS INTERNAL a
this is external a
THIS IS INTERNAL a
If the local a inside the loop survived to the next iteration then after the first print it should have printed only "THIS IS INTERNAL a" as the internal a would shadow the external a. The two strings alternating means that the internal local a never survives the bottom of the loop.
Of course, even if it did survive you would have the problem of the internal local declaration shadowing the previous iteration's local a each time through the loop. Essentially you would be unable to access the previous iteration's version of a and by the third iteration you would have 3 separate internal locals, only the last of which would be accessible. It would be the same as if you had done this:
local a = "THIS IS INTERNAL a"
local a = "THIS IS INTERNAL a"
local a = "THIS IS INTERNAL a"

lua variables scope

I am aware there are other similar topics but could not find an straight answer for my question.
Suppose you have a function such as:
function aFunction()
local aLuaTable = {}
if (something) then
aLuaTable = {}
end
end
For the aLuaTable variable inside the if statement, it is still local right?. Basically what I am asking is if I define a variable as local for the first time and then I use it again and again any number of times will it remain local for the rest of the program's life, how does this work exactly?.
Additionally I read this definition for Lua global variables:
Any variable not in a defined block is said to be in the global scope.
Anything in the global scope is accessible by all inner scopes.
What is it meant by not in a defined block?, my understanding is that if I "declare" a variable anywhere it will always be global is that not correct?.
Sorry if the questions are too simple, but coming from Java and objective-c, lua is very odd to me.
"Any variable not in a defined block is said to be in the global scope."
This is simply wrong, so your confusion is understandable. Looks like you got that from the user wiki. I just updated the page with the correction information:
Any variable that's not defined as local is global.
my understanding is that if I "declare" a variable anywhere it will always be global
If you don't define it as local, it will be global. However, if you then create a local with the same name, it will take precedence over the global (i.e. Lua "sees" locals first when trying to resolve a variable name). See the example at the bottom of this post.
If I define a variable as local for the first time and then I use it again and again any number of times will it remain local for the rest of the program's life, how does this work exactly?
When your code is compiled, Lua tracks any local variables you define and knows which are available in a given scope. Whenever you read/write a variable, if there is a local in scope with that name, it's used. If there isn't, the read/write is translated (at compile time) into a table read/write (via the table _ENV).
local x = 10 -- stored in a VM register (a C array)
y = 20 -- translated to _ENV["y"] = 20
x = 20 -- writes to the VM register associated with x
y = 30 -- translated to _ENV["y"] = 30
print(x) -- reads from the VM register
print(y) -- translated to print(_ENV["y"])
Locals are lexically scoped. Everything else goes in _ENV.
x = 999
do -- create a new scope
local x = 2
print(x) -- uses the local x, so we print 2
x = 3 -- writing to the same local
print(_ENV.x) -- explicitly reference the global x our local x is hiding
end
print(x) -- 999
For the aLuaTable variable inside the if statement, it is still local right?
I don't understand how you're confused here; the rule is the exact same as it is for Java. The variable is still within scope, so therefore it continues to exist.
A local variable is the equivalent of defining a "stack" variable in Java. The variable exists within the block scope that defined it, and ceases to exist when that block ends.
Consider this Java code:
public static void main()
{
if(...)
{
int aVar = 5; //aVar exists.
if(...)
{
aVar = 10; //aVar continues to exist.
}
}
aVar = 20; //compile error: aVar stopped existing at the }
}
A "global" is simply any variable name that is not local. Consider the equivalent Lua code to the above:
function MyFuncName()
if(...) then
local aVar = 5 --aVar exists and is a local variable.
if(...) then
aVar = 10 --Since the most recent declaration of the symbol `aVar` in scope
--is a `local`, this use of `aVar` refers to the `local` defined above.
end
end
aVar = 20 --The previous `local aVar` is *not in scope*. That scope ended with
--the above `end`. Therefore, this `aVar` refers to the *global* aVar.
end
What in Java would be a compile error is perfectly valid Lua code, though it's probably not what you intended.

Function/variable scope (pass by value or reference?)

I'm completely confused by Lua's variable scoping and function argument passing (value or reference).
See the code below:
local a = 9 -- since it's define local, should not have func scope
local t = {4,6} -- since it's define local, should not have func scope
function moda(a)
a = 10 -- creates a global var?
end
function modt(t)
t[1] = 7 -- create a global var?
t[2] = 8
end
moda(a)
modt(t)
print(a) -- print 9 (function does not modify the parent variable)
print(t[1]..t[2]) -- print 78 (some how modt is modifying the parent t var)
As such, this behavior completely confuses me.
Does this mean that table variables
are passed to the function by
reference and not value?
How is the global variable creation
conflicting with the already define
local variable?
Why is modt able to
modify the table yet moda is not able
to modify the a variable?
You guessed right, table variables are passed by reference. Citing Lua 5.1 Reference Manual:
There are eight basic types in Lua: nil, boolean, number, string, function, userdata, thread, and table.
....
Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.
So nil, booleans, numbers and strings are passed by value. This exactly explains the behavior you observe.
Lua's function, table, userdata and thread (coroutine) types are passed by reference. The other types are passed by value. Or as some people like to put it; all types are passed by value, but function, table, userdata and thread are reference types.
string is also a kind of reference type, but is immutable, interned and copy-on-write - it behaves like a value type, but with better performance.
Here's what's happening:
local a = 9
local t = {4,6}
function moda(a)
a = 10 -- sets 'a', which is a local introduced in the parameter list
end
function modt(t)
t[1] = 7 -- modifies the table referred to by the local 't' introduced in the parameter list
t[2] = 8
end
Perhaps this will put things into perspective as to why things are the way they are:
local a = 9
local t = {4,6}
function moda()
a = 10 -- modifies the upvalue 'a'
end
function modt()
t[1] = 7 -- modifies the table referred to by the upvalue 't'
t[2] = 8
end
-- 'moda' and 'modt' are closures already containing 'a' and 't',
-- so we don't have to pass any parameters to modify those variables
moda()
modt()
print(a) -- now print 10
print(t[1]..t[2]) -- still print 78
jA_cOp is correct when he says "all types are passed by value, but function, table, userdata and thread are reference types."
The difference between this and "tables are passed by reference" is important.
In this case it makes no difference,
function modt_1(x)
x.foo = "bar"
end
Result: both "pass table by reference" and "pass table by value, but table is a reference type" will do the same: x now has its foo field set to "bar".
But for this function it makes a world of difference
function modt_2(x)
x = {}
end
In this case pass by reference will result in the argument getting changed to the empty table. However in the "pass by value, but its a reference type", a new table will locally be bound to x, and the argument will remain unchanged. If you try this in lua you will find that it is the second (values are references) that occurs.
I won't repeat what has already been said on Bas Bossink and jA_cOp's answers about reference types, but:
-- since it's define local, should not have func scope
This is incorrect. Variables in Lua are lexically scoped, meaning they are defined in a block of code and all its nested blocks.
What local does is create a new variable that is limited to the block where the statement is, a block being either the body of a function, a "level of indentation" or a file.
This means whenever you make a reference to a variable, Lua will "scan upwards" until it finds a block of code in which that variable is declared local, defaulting to global scope if there is no such declaration.
In this case, a and t are being declared local but the declaration is in global scope, so a and t are global; or at most, they are local to the current file.
They are then not being redeclared local inside the functions, but they are declared as parameters, which has the same effect. Had they not been function parameters, any reference inside the function bodies would still refer to the variables outside.
There's a Scope Tutorial on lua-users.org with some examples that may help you more than my attempt at an explanation. Programming in Lua's section on the subject is also a good read.
Does this mean that table variables are passed to the function by reference and not value?
Yes.
How is the global variable creation conflicting with the already define local variable?
It isn't. It might appear that way because you have a global variable called t and pass it to a function with an argument called t, but the two ts are different. If you rename the argument to something else, e,g, q the output will be exactly the same. modt(t) is able to modify the global variable t only because you are passing it by reference. If you call modt({}), for example, the global t will be unaffected.
Why is modt able to modify the table yet moda is not able to modify the a variable?
Because arguments are local. Naming your argument a is similar to declaring a local variable with local a except obviously the argument receives the passed-in value and a regular local variable does not. If your argument was called z (or was not present at all) then moda would indeed modify the global a.