Hi all,
Here is a little piece of code I'd like to share. This is my own crack at the
undefined variables problem. This is defnitely not something new, but a new approach to work-around the problem.
With the Lua programming language, undeclared variables are not detected until runtime, as Lua will not complain when loading code. This is releated to the convention that Lua uses : global by default. In other words, when a variable is not recognized as local, it will be interpreted as a global one, and will involve a lookup in the global environment, which is
_G (for Lua 5.1). Note that this behaviour has been addressed in Lua 5.2, which strictly speaking has no globals, because of its lexical scoping.
strictness is a module to track access and assignment to undefined variables in your code. It enforces to declare globals and modules variables before assigning them values. As such, it helps having a better control on the scope of variables across the code.
strictness is mostly meant to work with Lua 5.1, but it is compatible with Lua 5.2.
Here is an example of use, plus some code:
The module is required added into your project this way:
local strictness = require 'strictness' |
Then you can use the functions provided with the module to patch tables (or modules). A patched table become "strict", meaning that trying to access to some undefined field inside this table will raise an error.
Let us consider this little example. We create a table with two fields, named "x" and "y". We want to get the value at field "x", but instead, we index field "X" (small typo).
local player = {x = 10, y = 10}
print(player.X) --> nil, as "player.X" (undefined) is not the same as "player.x" |
Obviously, the value returned is
nil and this will remain silent until this value is involved in some computation where it would probably err.
However, in case the table player was made
strict, an error will occur right at the line where we wanted to index the undefined field.
local player = {x = 10, y = 10}
strictness.strict(player)
print(player.X) --> error: Attempt to access undeclared variable "X" in <table: 0x0032c8e8>. |
Added to that,
strictness now offers the possibility to run functions in strict mode. In this mode, a function is not allowed to access/assign values to undefined variables in its environment table.
local env = {} -- a blank environment for our functions
-- A function that writes a varname and assigns it a value
local function normal_f(varname, value) env[varname] = value end
-- Convert the original function to a strict one
local strict_f = strictness.strictf(normal_f)
-- set environments for functions
setfenv(normal_f, env)
setfenv(strict_f, env)
-- Call the normal function, no error
normal_f("var1", "hello")
print(env.var1) --> "hello"
strict_f("var2", "hello") --> produces an error, as "strict_f" cannot write in its environment. |
Latest version :
0.2.0Source:
githubDocumentation:
online module docsTutorial:
Getting started
Comments
Somehow I never get into situations like the typos so don't really see a problem (probably because I almost never use autocompletes)
but the idea of using such strictness in development or debugging mode is great
Hi @ar2rsawseen
If I for example make the follow in Gideros:
ie when I use the scene manager, "X" will be removed in the next scene, of course if "X" is a local variable.
I read in this link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
Undeclared variables are always global, in JavaScript
Any information thanks
--------------Edit--------------
If I put only "X" I got this:
"main.lua:4: '=' expected near 'end'
I believe that such errors have been expected and prevented from occurring when compile the code, but if I use the next code:
[-] Liasoft
Good point. My example might be a little off, but those kind of issues are much more likely to happen with large projects, especially when you have to deal with multiple files. Typos are very likely to happen, no matter how experienced one is. I do know something about that
@HubertRonald: In your first example, the code itself is not correct. Since "X" is not a valid Lua statement, the code won't even compile.
In the second example, though, you define a local variable and assign it a value, which is 3. Then right after, you will create a global variable, named "X", which will be avilable from anywhere else in the code, as soon as the function MyClass.init() is called once.
This new global "X" will be assigned the value 4 (which is the value of local "x" plus 1).
So, maybe you were expecting to increment the local variable "x", finding this odd might be quite nifty.
Using *strictness*, this is unlikely to happen, since when calling MyClass.init() for the first time, it will detect that "X" is an undeclared global, and an error will be raised, so that you can fix the typo...
I tried the following code:
Thanks again for share and comment
[-] Liasoft
No, it doesn't work that way.
First of all, you printed all the contents of the global environment _G.
Then, you defined a global object, named MyClass (instance of Gideros' Core.class), and appended a method named init() to it.
Then right after, you deleted that object. Therefore, you cannot find any global "X" because the init method was never run.
Run it, even once, you will definitely spot a new global "X".
I tried the following code:
Amazing what I have learned today
Thanks @Roland_Y keep it up!
[-] Liasoft
Undeclared variables are always global, in Lua too
This is similar to JavaScript
[-] Liasoft
http://www.luafaq.org/#T1.6
https://github.com/gideros/gideros
https://www.youtube.com/c/JohnBlackburn1975
Yes, that is true, but just for Lua 5.1, though. Actually, when Lua wants to access/assign a variable, and cannot find a local (or an upvalue) within the scope of the current code context, it ends up looking up in the global environment _G: that is what makes it a global.
In Lua 5.2 though, this is not the same. Actually, Lua 5.2 (strictly speaking has no global) has a lexical scoping scheme. _G is still there, but actually, an undefined variable raises a lookup in a different table, _ENV, which can be _G, or can be redefined in a new code segment. That feature is quite powerful
@john26 : Thanks for the kind words. Actually, this library was inspired by strict.lua. Although, I wanted something better because strict.lua only patches the global table _G and set it a custom metatable. In case your current code has a metatable appended to _G, strict.lua would override it. Also, I wanted to be able to spot access to undefined fields in any random table, and strict.lua would not allow that. Last, strict.lua only catches undefined global only in the main program, via the debug library. I tried to build something that would provide some of the missing features (IMHO) in strict.lua through this.