I know in Lua nil is tricky because it can mean both/any of nil or undefined. But this interface should show all the keys defined on this table, not all the values. So I think it can work better.
Also I noticed that the debugger is not populating all the local variables of my Module Script. For instance, BaseSwordDef is missing.
But this interface should show all the keys defined on this table(âŚ)
It does. Setting a nil value for a key in a table is not a definition. You could argue that it is a declaration, but in my opinion itâs not even that.
The compiler wonât assign memory for declared but undefined variables (since it doesnât know how many bytes are needed duh) until the actual definition happens, which is why it doesnât show up in the debugger.
It is usually considered bad practise to declare variables and not initialising them on the same line.
As for the missing locals the compiler seems to strip debug information from upvalues which it shouldnât when debugging. Declaring those upvalues as globals will let you watch them though.
Adding a watch for something that is in scope feels bad.
What if you had to do that in MSVC?
Maybe add a tab that is for âEverything that is currently in scopeâ. But make it the default tab, because thatâs all anyone is ever going to want to look at.
Setting a value in a table to nil will not allocate any memory to it since it is not a definition but at most a declaration, as I said before. It is not any different in C and C++.
int var1; // declaration, no memory assigned and removed by the compiler if left as is
int var2 = NULL; // in C: initialises with the value of (int)(void*)0 which is the integer 0. Since lua does not have pointers, nil does not represent the null pointer and therefore cannot cast it to 0
int var3 = 0; // declared, defined and initialised
Lua is tricky. Unlike languages like C# where everything is defined and stays permanent even if a variable is nothing, in Lua, if you set a key in a table to nil, it doesnât just set the key to nil, it also erases the key itself.
Letâs say I created a table with a single index:
local t = {
Foo = "Bar"
}
And then I set that key to nil.
t.Foo = nil
It completely erases the field, so the table is now the equivalent of this:
local t = {}
Thatâs why you donât see the fields in the debugger.
This is a design flaw in Lua that seems to go very deep. For instance,
Iâm explicitly adding these nil entries to my table âobjectsâ in the hope that Roblox Studio intellisense will pick them up and prevent me from typo-ing them in a future, creating runtime errors.
Look at this crazy stuff
Guess what this outputs:
local count = 0
local t = { why = nil, whynot = 1, y = {}}
for _ in pairs(t) do count = count + 1 end
print(count, #t)
Lua is the only language I know of where you have to think about it. The answer may surprise you.
This is a problem with how # is implemented, not how tables actually work. On top of that, I donât see any scenario where you would need to use # on a mixed table or an array with empty values. For the method the # is implemented there will always be someone out there who wonât like it so itâs also useless to complain about it.
This isnât a design flaw, youâre not supposed to work around it, youâre not supposed to make that # work, whatever is the number you need, you either need to keep record of it yourself somewhere or calculate it when you need it.
Your data structuresâ values should be explicitly defined, you canât make an arbitrary data structure (like in that example) and expect the # to guess exactly what it is you want. Using it outside of its scoped and intended use and then calling it a design flaw is not right.
Itâs not, you have to think about it in C++ datatypes, JavaScript arrays, Python sets and ectâŚ
Nil is not undefined, when anything is set to nil, it means any value held will be deallocated in some time and the key will be destroyed just like ForbiddenJ said, this the expected behavior of any datatype in Lua since there is no actual memory deallocation in Lua so setting it to nil means completely getting rid of it, including the space it held. In Lua, you cannot preallocate data (There are some tricks to confuse gc to make some code more efficient by not letting it deallocate but those are hacks and are outside of the intended use of Lua) so the debugger is right in not seeing them since they donât actually exist in memory.
Disagree. When I use Lua its like Iâve fallen back into the 1980s.
This is how you get a table size in C#:
Hashmap.Keys.Count
Importantly, this performs no computation (O(1) vs O(N) for #) and it is obvious by looking at it how it works. Also the value part of the key/value pairs donât matter. If they are null they still exist. I can serialize the data structure without writing extra code to trap this case.
I donât want to manage basic data structures myself. I want to write games. The fact that I need to represent my structs as tables makes this harder.
This short-coming is part of a pattern of general inadequacy for using dynamically typed tables to represent game objects and how it makes things that are easy in other languages difficult/annoying in Lua.
Again, thereâs no distinction between null and undefined in Lua. This is probably a design flaw, but the decision was made long ago. You could make a feature request for some equivalent to null (or support an existing one if it exists) to fill that gap if you have a compelling enough use case, but as it stands thereâs no easy way to distinguish between the two.
That said, you might try using the Typed Lua beta and making feature requests to help shape it to your use. Itâs more likely that youâll get a way to mark a field as uninitialized to the script analyzer than it is that youâll get a new keyword, so I would recommend this route.
If you have qualms with the behavior of the # operator thereâs an existing feature request to make it do what I think you want here:
Isnât the example completely unrelated to game object representation?
In general it feels like youâre comparing apples and oranges when comparing data structures in C# vs Lua. The odd behavior of # comes primarily from the fact that in Lua right now, table length grows automatically when you assign elements to indices (this is common in many other dynamic languages like JavaScript), and shrinks automatically when you remove elements (this doesnât happen in JavaScript for example).
We might change the semantics of # at some point, thereâs a few alternatives here that Iâve been thinking about.
Maybe, but itâs actually a bit complicated. This is a wart, but fixing this wart makes one more appear (the differences between undefined & null arenât pretty, although in JS itâs more odd because an array cell can be empty, undefined or null!). The balance is tricky. Overall other than # this doesnât seem actively bad - and I think we can make # nicer in time.
I can relate there. I know another developer who still programs websites in VBScript, which is ancient now.
Lua was designed with flexibility in mind, so it may be possible to write a basic typing library to force lua to behave the way you need, so you could do something like this:
local TL = require(game.ReplicatedStorage.TypeLib)
local MyClass = TL.newClass({
Ball = {"BasePart"},
PreferredNumber = {"number", 3.5},
Bounce = {"string (number, string)", function(self, count, thenSay)
-- Do whatever here
end}
Constructor = {"(BasePart)", function(self, ball)
self.Ball = ball
end}
})
local v = MyClass.new(Instance.new("Part"))
v:Bounce(3, "Hello")
v.Ball = "Something Else" -- Error: Attempt to write string to BasePart value Ball.
Not pretty, but itâs the best I could think of off the top of my head.
Iâm inclined to agree â Iâve only rarely come across times where the distinction would be helpful. It might be worth doing for Luau though just because there is a distinction worth making for analysis purposes. In this case, for example, it would make life easier if there was a way to set somethingâs value to exists but is empty. Not sure if thatâs something the functional part of the language should support though; might be better to make it part of the type system.
This is a bit off topic, since weâre start talking about functional changes, it made me think of it. Luau is becoming less and less like stock Lua. Between things like type annotations and language features like continue, at what point is Roblox Lua a âsubsetâ language that should have its own file extension and whatnot?
I donât think we want Lua to become more like JavaScript. Luaâs simplicity is one of its few advantages compared to other languages, and weâll begin to lose this advantage if Lua is pushed to conform to another languageâs standards (like by adding undefined, or a ârightâ way to do OO.)
Perhaps table prototyping could be done by the VM, and fields that are defined explicitly (e.g. {Foo = nil}) could be allocated such that they use 16 bytes for each field instead of 40, and so nil values are visible when debugging? This would be a pretty cool optimization that also fixes the debug problem, and I think something like it was mentioned during RDC, but for a quick fix one could use false instead of nil.
A solution would be to add an âemptyâ type and global, which would be logically falsey, but only set explicitly, so no change to the behavior of nil
The biggest issue is that the â==â operator is useless. Otherwise I donât think this would be a big problemâŚ
In Lua nil absolutely does mean undefined, the fact that these mean the same thing is a core part of the languageâs semantics, which is definitely not feasible to change. This goes as far as the way to shrink a table being setting the values in the table to nil.