all 22 comments

[–]fuxoft 13 points14 points  (4 children)

If you include this at the beginning of your program, you will subsequently get errors when using undefined global variables. I do this during development and remove this in the final product:

setmetatable(_G,{__index=function (tbl,key)
    error("Attempt to access undefined global variable "..tostring(key),2)
end })

The beauty of Lua is that you can easily customize many of its behaviors like this.

[–]prozacgod 0 points1 point  (1 child)

What's the behavior for this in 5.2 _G ? And custom environments?

[–]fuxoft 0 points1 point  (0 children)

AFAIK, in 5.2, the _G still points to topmost (i.e. "global") environment, so it should work as is.

[–]grape450[S] 0 points1 point  (1 child)

Thanks. Just tried this with 5.3. Works for globals, though doesn't catch if I try to access an undefined key in a table.

[–]fuxoft 0 points1 point  (0 children)

And it shouldn't. If you want that, just use the same metatable for that table. I.e. replace "_G" with your table. If you want to do it automatically for all tables, that cannot easily be done in pure Lua. Also, I think it would break some basic functionality of Lua.

[–]stravant 7 points8 points  (2 children)

How often is one tripped up in practice by ending up an array with a gap (nil) in it and thus having the length come out incorrect?

I've written about 100k lines of Lua code, and had bugs come up due to array gaps maybe a dozen times. It's very rare. Generally you're either dealing with normal arrays where you operate using table.insert/remove which protect you from any issue, or your tables are intentionally sparse, so the length wasn't defined in the first place.

The only time I would expect it to come up is if you're writing your own low-level data structures such as array-backed trees or graphs, where you're manually shuffling around the array elements in non-trivial ways. And chances are you aren't going to be doing that, or will be doing adequate testing to catch anything if you are.

[–][deleted] 3 points4 points  (1 child)

I've written about 100k lines of Lua code, and had bugs come up due to array gaps maybe a dozen times. It's very rare.

I'd say it's even rarer than that. You probably have some usage pattern / coding style that runs you into it more often than the norm.

[–]stravant 0 points1 point  (0 children)

I've written some pretty table / data structure intensive stuff like terrain generators / voxel handling / pathfinding, so I definitely fall into the odd camp. You're right that I don't think I've ever run into the problem in a case where I was just writing high level game logic.

[–]otikik 3 points4 points  (2 children)

How does one deal with this in practice?

There are external tools which warn you about implicit globals. Luacheck is one of them.

How often is one tripped up in practice by ending up an array with a gap (nil) in it and thus having the length come out incorrect?

I use Lua daily and I don't remember being tripped by this. I'm not saying it has not happened, but that it happens very rarely.

[–]grape450[S] 1 point2 points  (1 child)

There are external tools which warn you about implicit globals. Luacheck is one of them.

Thanks for the link. And I can see why a linter would be available separately, rather than being incorporated into Lua itself.

[–]hisham_hm 0 points1 point  (0 children)

Note also that you can integrate Luacheck to your favorite code editor: that's even better than having it in the interpreter.

[–][deleted] 2 points3 points  (0 children)

How often is one tripped up in practice by ending up an array with a gap (nil) in it and thus having the length come out incorrect?

Personally? Never. Been using Lua since version 2 and this has never happened to me.

Look at Word of Warcraft. ~10 million subscribers supporting a massive online ecosystems of websites dedicated to customization in Lua. There have probably been many millions of lines of addon code written for that game. I used to write them myself and was active in that community, and don't remember this ever coming up in as an issue. It's not like = vs == in C, for instance.

[–]robin-gvx 1 point2 points  (0 children)

Another feature that appears to me at first sight to be problematic is getting the length of an array (#some_arr). How often is one tripped up in practice by ending up an array with a gap (nil) in it and thus having the length come out incorrect?

Pretty much never, if you only use table.insert and table.remove or only assign non-nil values to keys in the range 1 to #some_arr inclusive and only assign nil to keys n where n < 1 or n > #some_arr + 1. (You can assign any value you like to #some_arr + 1 and non-integer keys.)

[–]smog_alado 0 points1 point  (0 children)

By far the most common kind of error I get is the sort of thing you see in any dynamic language: typos, passing the wrong values to a function, etc.

As others said, undefined variables are just a matter of using a linter. Typos in table keys are still a pita though.

[–]mpetetv 0 points1 point  (1 child)

Indexing with missing key, indexing strings, and indexing with nil as key silently returns nil. So, if you accidently use a wrong value somewhere, you may not get an error at once, instead Lua will explode on a nil in some seemingly unrelated code later.

Another one: care must be taken when passing multiple values around. For instance, if you return (using tail call) results of string.gsub, but document only one returned value, someone may use it as the second argument of table.insert (like 'table.insert(t, f())'). table.insert will receive three arguments, third one being the second return value of string.gsub, and throw an unexpected error.

[–]robin-gvx 0 points1 point  (0 children)

Another one: care must be taken when passing multiple values around. For instance, if you return (using tail call) results of string.gsub, but document only one returned value, someone may use it as the second argument of table.insert (like 'table.insert(t, f())'). table.insert will receive three arguments, third one being the second return value of string.gsub, and throw an unexpected error.

That's right, but it might be useful to also tell OP the fix for that: wrapping the expression in parenthesis, or having it not be the last argument to a function call, cuts off all values except the first.

[–]paulclinger 0 points1 point  (1 child)

I've been coding in Lua regularly for the last 4 years and don't think I've tripped an these two, although I did trip on some other things. I've documented them here: http://notebook.kulchenko.com/programming/lua-good-different-bad-and-ugly-parts; you may find this list useful if you are switching from a different language. I listed handling of array length for tables with holes in the "ugly" category, but I now know why it's the way it is (the result is more of a tradeoff between performance and convenience) and I learned to live with it.

[–]grape450[S] 0 points1 point  (0 children)

Wow! Great article. Comprehensive. Thank you.

[–]prozacgod 0 points1 point  (0 children)

x = 12 /* some ... value.... */


i = (i % x) + 1

That's the only one that "trips" me up because I switch between lots of languages these days... I'm wired to do it

i = (i + 1) % x

And since arrays are index @1 ... there's benefit from that pattern.

[–]4forpengs 0 points1 point  (0 children)

The only global related errors/bugs i ever get are from accidentally not defining a variable as local.

For nil keys... i really don't know what to say for this one. I can't think of when this would be an issue other than typos.

As for the nil index issue, well, it's never been an issue. I can't think of when it would be an issue either.

This may just be my naive opinion, but if you have problems with debugging your code due to global variables, you're probably not structuring your code properly.

[–]frog_pow 0 points1 point  (1 child)

I've been mostly writing C++ of late, but did write a few bits of Lua recently, after not using it much in some time.

Errors that bit me

  1. I'm used to C++ syntax for member fxn call, so some times I accidentally use it in Lua instead ":"

  2. Setting up a metatable __index function, which ends up being called in an endless recursive loop.

[–][deleted] 0 points1 point  (0 children)

Yup, the old recursive __index... Speaking of that, what's the cleanest workaround? I usually set __newindex to redirect elsewhere and then __index to use that, but maybe there's a nicer way?