> It's definitely surprising that globals default to nil, [..]
If you've worked a bit with Lua, it's actually pretty reasonably and consistent: Everything is a table and accessing unset variables is accessing the table (named _G) holding all global variables. So
print(a)
assuming a isn't a local variable, is essentially the same as
print(_G['a']) -- or print(_G.a)
As accessing unset keys within a table returns nil, doing so for globals makes it consistent. Also _G._G == _G :-)
Since Lua 5.2 non-locals are loaded from the table/object _ENV. Every lexical context has either an implicit _ENV, or an explicit _ENV (e.g. nearest `local _ENV = ...`). The default value for implicit _ENV is the global table (i.e. _G, but not literally _G; it's initialized as if from lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD)). See https://www.lua.org/manual/5.4/manual.html#2.2
Lua 5.1 had something similar, but the implicit environment was per-function.
A common pattern in Lua is to change _ENV to an empty table with a __newindex meta-method that writes-through to the real global environment, and an __index meta-method that throws an error if the key (i.e. global symbol) doesn't exist. You can fancy and have an __index meta-method that that throws an error string with suggestions for a possibly misspelled symbol, similar to clang or GCC compiler diagnostics.
At this point, I've worked 'a bit' with Lua, in addition to plenty of other coding experience, and it's still surprising to me.
> Everything is a table and accessing unset variables is accessing the table (named _G) holding all global variables.
Before you said this, I had no idea about that. Because it's not like the tutorials are talking about the underlying implementation for this stuff, just like how Java tutorials don't usually go deep in explaining how bytecode works, or how the JVM can be used to interoperate with other languages.
Lua is an incredibly simple and elegant language. All the myths and bike-shedding on Stack Overflow and elsewhere can only lead you astray. For exceptional situations, use the mailing-list. The authors and almost all experts are on the mailing-list and will be quick to correct errors. (Everybody is wrong sometimes, even the authors! But you need enough experts in one place to be paying attention, and the only such place is the mailing-list.)
Another thing to keep in mind is that Lua maintains strict feature symmetry between what's possible in Lua code, and what's possible from the Lua C API. This is important to understand if you want to have a good mental model of the language and why it's designed the way it is. If you're not a C programmer or haven't heavily used the Lua C API, many of Lua's choices may seem arbitrary, especially coming from other languages where their C ABI and FFI interfaces are mostly an afterthought or don't otherwise constrain the in-language semantics. Most "better than Lua" projects are written by people without a sufficient appreciation for this aspect of Lua's design.
So just to be clear here, the things you're recommending a nine year old to do are: ignore tutorials in favor of reading directly from the language reference manual, and keeping in mind the language's C API parity.
> This is important to understand if you want to have a good mental model of the language and why it's designed the way it is.
I want to have a good mental model of the code I write, and how that works, not the underlying language mechanisms themselves.
I've been picking up Kotlin, and I hardly know anything of how it works under the hood, other than whatever it 'inherits' from being a JVM language, and so far it's been totally fine. Why should I need to care about how the sausage is made?
> the things you're recommending a nine year old to do are
Are you the nine-year-old? I was responding to a criticism you made regarding your understanding. Are online tutorials for children learning their first programming language supposed to explain the rationale behind the language design? If you want the latter don't use children's tutorials, or in the case of Lua, any tutorials, because the official material is at least as easy to understand, and infinitely more reliable and accurate.
Sure, which is why I said "surprising" and not "inconsistent" or "weird". New Lua devs are likely to have to debug an issue caused by typing "ponits" instead of "points" well before they get to the point that they learn about the global table.
If you've worked a bit with Lua, it's actually pretty reasonably and consistent: Everything is a table and accessing unset variables is accessing the table (named _G) holding all global variables. So
assuming a isn't a local variable, is essentially the same as As accessing unset keys within a table returns nil, doing so for globals makes it consistent. Also _G._G == _G :-)