5 ms·
One problem I have with Lua is that it silently returns nil when accessing non-existent elements. There are a few hacks around this, but the most common only pr
by vor_ 13y ago
One problem I have with Lua is that it silently returns nil when accessing non-existent elements. There are a few hacks around this, but the most common only protect global access. I have to wonder why the language reports no error in that situation given that it does report an error when comparing values of different types (no automatic conversion) or when attempting to index nil.
- pixie_ 13y agoDoes that make it like Objective-C because I love how accessing a null object doesn't blow up the application. So many trivial bugs in my iOS app that would have caused the entire application to crash are totally avoided. Unless you're creating software for the space shuttle, avoiding null exceptions entirely is a god send for my users.
- vor_ 13y agoIt's not like Objective-C, which returns a nil value when sending a message to nil. Lua, on the other hand, throws an error when attempting to index nil.
- anonymoushn 13y agoYou can change this to be more like Objective-C: debug.setmetatable(nil, {__call = function() return nil end, __index = function() return nil end}) f = nil print(f(f.bar.baz)()()()) -- nil This seems like an especially awful idea to me, though.
- SimHacker 13y agoStrongly disagree. You should fix trivial bugs before you ship the app, and Objective-C silently hiding them from you makes that much harder. It's one of the things I hate about Objective C. Makes it seem more like PHP with warnings off.
- pixie_ 13y agoWhy is it programmers love crashing their users for the most trival things? What if every javascript bug crashed the website you were browsing?
- SimHacker 13y agoEthical programmers don't want to deliver programs to users that are riddled with unknown, undetectable bugs, that silently corrupt data and make mistakes, without being caught by testers or warning the user. That's why it's better to be bold and crash spectacularly, instead of trying to cover up unexpected problem by guessing. If you have an unexpected null pointer, there is a reason, and your expectations were wrong, so there's something bad going on that you don't know about. By silently hiding the problem, your program may be doing the just wrong thing, and you never know about it. That's the problem with PHP, which has it even worse than Objective C. Ignoring errors and guessing what to do is PHP's way of life, and it's been the cause of countless security holes, data corruptions, and hard to diagnose bugs. Use lots and lots of pedantic asserts, and test thoroughly, so the production code does not have to be riddled with labyrinths of error handling and mitigation guessing code. Assume the APIs obey their contracts, and write APIs with strong contracts that do what they promise. If something might return a NULL, then test for it and handle it properly, so whoever is reading and maintaining your code knows that you considered the possibility and thought through the solution, and the computer doesn't have guess what to do. Computers are notoriously bad at guessing the programmer's intentions, so programming languages should not encourage programmers to let the computer guess what they meant.
- gruseom 13y agoit silently returns nil when accessing non-existent elements Is that similar to what JS does with undefined, or am I missing something?
- _dps 13y agoIt is mostly the same, though in my experience it is easier to stop this in Lua than in JS (just attach a metamethod to the globals table to raise an error on lookups for keys with undefined values... this is similar in practice to JS strict mode). I'd add that most worries about silent nil returns stem from not accepting Lua's very clear value semantics; nil literally means "undefined value" (as compared to JS's null or Python's None); setting a variable to nil is the idiomatic way to release its value for garbage collection. So "silently returning nil" is more like "explicitly returning notice that the value is undefined". Whether you want that to be an error-raising-event or not is up to you, the programmer. And it's easy to control that error-throwing behavior in a granular way; you can attach the error-throwing metamethods to the globals table, or to specific tables you care to protect.
- kemayo 13y agoIt's because accessing a variable means looking up a name in the global environment, which is technically just a table. Since it's just a table like any other, any non-set key has nil as its value. You can actually access said global environment as the _G variable. Now, you could totally stick a metatable on _G that caused some sort of exception when you tried to get the value for a key with a nil value.
- vor_ 13y agoI understand the reason for the behavior--Lua pretends that new tables start with all possible keys set to nil. I find it regrettable because it necessitates static analyzers for larger projects and contributes to a lack of safety, especially since the language is targeted at non-professionals and hobbyists. With multiple contributors to a project, one typo can lead to unintended sparse arrays and hard-to-debug errors that propagate far from their origins. The behavior seems inconsistent with Lua's existing policy of raising errors for attempted indexing of nil and for order comparisons of different types of values. The issue was raised on the Lua mailing list, and the response from one of Lua's curators was, literally, "I used to make mistakes like that, but then I got used to it." Lua is admirable for its size and speed, but I believe that it's sometimes too conservative about changing historical behaviors to accommodate safer policy.
- anonymoushn 13y agoWow, accidentally putting holes in the middle of a numeric table sounds nasty. These issues don't bite me very often, but they are pretty easy to prevent. function a_stricter_table() return setmetatable({}, {__index = function(t,k) error("no value at "..tostring(k)) end}) end t = a_stricter_table() t.a = 5 print(t.a) -- 5 print(t.b) -- crash D: A similar trick can make trying to unset a value by assignment raise an error, while unsetting a value using a delete() function is allowed. Edit: Removed the fake table/backing table thing, since you can do the one trick without it. I think the other trick requires it, though.
- vor_ 13y ago
- copx 13y agoIt is the norm (for larger projects) to use a "strict" script which prohibits the use of undeclared globals. As far as local nil access is concerned, it is necessary for idiomatic Lua. For example, this is how you implement optional arguments and default values in Lua: NeumannNeighborhood = function(position, extent) extent = extent or 1 ... end Here extent is optional and its default value is 1. That's how you keep a language small.
- vor_ 13y agoYour example wouldn't be affected by having Lua raise an error for undefined variables, as "extent" is defined in the argument list. Suppose that you accidentally wrote: NeumannNeighborhood = function(position, extent) extent = extant or 1 ... end It would always be assigned 1, and you'd never be told about the error.
- copx 13y agoextent is declared, not defined. You wrote "accessing non-existing elements" .. extent may not exist in my example. And as I said bigger Lua project usually use a script which prevents the use of undeclared globals. Such a script would detect the bug in your example because "extant" is a global and undeclared (variables are global by default in Lua).
- vor_ 13y agoIt's declared and defined in the function argument list, assigned nil if no argument is passed for it. The problem with prohibiting the access of undefined globals is that your tables remain unprotected. If the value of "extent" was retrieved from a table argument using an incorrect key, the mistake would go unreported.
- copx 13y ago>It's declared and defined in the function argument list No, it isn't. No value is ever assigned to "extent" if the function gets called without a second argument. "extent" is nil just like all other non-existing objects are nil. It is a common newbie mistake to treat Lua's nil as the equivalent of a null pointer or some kind of "nothing value" you find in other languages. Lua's nil is not a value of any kind. "object = nil" means "delete object" not "set object to nothing value". Because of that you should never assign nil to anything unless you actually want to delete it. Delete as in "garbage collector please pick this up". E.g. character.armor = NULL in C does not delete the armor field of the character struct. character.armor = nil in Lua does.. and you probably don't want that in such situations because if you handle it this way you create countless unnecessary allocation, reallocation, and deallocation operations behind the scenes. If you just mean "nothing here right now" you should set the field to "false" or some specific nothing value. >If the value of "extent" was retrieved from a table argument using an incorrect key, the mistake would go unreported. That is correct and a problem indeed. However, in my experience such things rarely happen and the resulting bugs are easy to find.
- justincormack 13y agoAdd a metatable to the tables where undefined access must not be allowed. a = setmetatable({...}, {__index = function(i) error "undefined" end}) is all you need.