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.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.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).
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.
If you prefer the Lua concept that all possible elements start out pre-set to nil, then again, "extent" is already defined. Whichever mental approach you prefer, the variable "extent" exists in your example.
> 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.
This isn't correct. Nil is a value type, and it exists solely to be different from any other value. Lua tables are presented as containers in which every possible key starts out already assigned the value of nil. Nil doesn't explicitly mean "delete object". Rather, a table variable is a reference to a table object, so assigning it a value of nil removes the reference and makes the object available for garbage collection (if nothing else is referencing it).
In other words, the reason Lua makes no distinction between non-existent elements and elements assigned to nil is that, conceptually, every possible element already exists and starts out assigned the value of nil. Therefore, when I use the term "non-existent", I'm speaking in practical terms from the perspective of the programmer. You presented to me an example which you claimed would no longer work under the behavior I proposed, but the "extent" variable would be both declared and defined in the function's argument list, so the "default argument" behavior would still work. The variable would just be assigned the value of nil if no argument was passed for it.
I'm not sure why you're offering these explanations except to make it appear as if I don't know Lua. I presume you're the one who voted down my previous comment.
> That is correct and a problem indeed. However, in my experience such things rarely happen and the resulting bugs are easy to find.
To the response that it just doesn't happen that often or that the bugs can be easily fixed, I would point out the existence of several Lua static analyzers and direct you to Google's own analyzer (http://code.google.com/p/lua-checker/), with this project description:
"Lua is dynamically typed, with a simple type model. Variables can be assigned values of any type. This makes development of small scripts somewhat easier, but it means that in larger programs many common programming errors are not detected until run time. For example:
- Referencing a variable that has not been declared will return nil. This is no different from referencing a 'declared' variable that has the value nil. Thus spelling mistakes in Lua variable names can go undetected and lead to program misbehavior.
- Tables (Lua's main data structure) are not typed, so any table can contain any keys. When tables are used in a manner similar to C structures, spelling mistakes in field names can go undetected and lead to program misbehavior.
- Function arguments and return values are also untyped and have similar problems.
In practice, Lua programs over (say) 1000 lines tend to accumulate these kinds of problems, making debugging difficult."
a = setmetatable({...}, {__index = function(i) error "undefined" end})
is all you need.
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.
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.
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.
Is that similar to what JS does with undefined, or am I missing something?
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.
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.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.