lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



Le lun. 6 sept. 2021 à 18:44, Bas Groothedde <lua@xoru.net> a écrit :
type requires any value, so not LUA_TNONE. The fact that "none" gets coerced to "nil" upon assignment (because otherwise the assignment breaks), doesn't really mean that a call like you describe should work in my honest opinion.

Isn't the error generated by "type()" superfluous when it is called with no argument at all? Shouldn't it simply return nil so that "type(('A'):byte(2))" will safely also return nil without any error?

I imagine this situation where functions like these are declared:
    function fun1(tab, index)
        return type(tab[index]), 'bad type of element in table'
    end
    function fun2(tab, index)
        error(type(tab[index]), 'bad type of element in table')
    end
    function fun3(tab, index)
        return nil, FIXME(type(tab[index], 'bad type of element in table'))
    end
where the function may inspect the types of elements in a given table at random index position and then decide to do conditional code based on the detected type, and wants to return this type string (or raise an error where the error values must contain that type like in the second function).
Both won't work as expected because that type string will not be present on the first position, but unexpectedly replaced by the 'bad type of element in table' string (or will also cause the type() function to also raise its own error, and the actual explicit "return" statement in "fun1", or the actual explicit call to "error()" in "fun2", or the actual explicit call to " FIXME()" in "fun3" won't be executed).

So instead, to get the expected result, we currently have to write:
    function fun1(tab, index)
        return type((tab[index]))
    end
    function fun2(tab, index)
        error type((tab[index])), 'unsupported type'
    end
    function fun3(tab, index)
        return nil, FIXME(type((tab[index], 'bad type of element in table')))
    end
The extra parentheses around the parameter(s) given to type() look very tricky! A programmer (or an IDE) may think that these parentheses are superfluous and would discard them.

I don't see ANY benefit or any use for the error generated by "type()" when it's called without any parameter, it should just simply return nil. Using type() in Lua is very common for functions already making complex conditional execution based on the types of objects it accepts and inspects (these may be complex analysis using recursion inside tables, inspection of metatables, inspection of function environments, and so on).

These "dummy" parentheses are quirky. Some programmers use extra parentheses everywhere which do not clarify the code, when there are deeply nested in expressions and are not even needed such as:
   if (((a == 1) and (b == 2)) or ((c == 3) and (d == 4)) then ... end
which is simply:
   if a == 1 and b == 2 or c == 3 and d == 4 then ... end

[note]
  Programmers should know or learn the priority of operators used in expressions...
  Add function calls like type() and you have too many parentheses and it actually gets harder to guess visually how they are paired to create subexpressions.
  We see lot of those dummy parentheses also in other languages. Including in C/C++ with the false assumption that these parentheses for subexpressions would force their order of evaluation, and we have IDE's using refactoring tools that will help to automatically remove all those unneeded and indesirable parentheses
[/note]

I just hope that IDE's or program editors (if they are used for Lua and know its syntax) will know that parentheses around Lua function calls are important: these "dummy"-like parentheses must be preserved when the closing parenthesis is not immediately followed by a comma, or by another operator, or by another closing parenthesis ")" or bracket "]".
This exclusion of the comma is also not effective when a function call between these "dummy" parentheses is followed by a trailing comma inside a table constructor. So the following does not work:
    table1 = { 1, type(_expression_) } -- may generate a runtime error

We must use:
    table1 = { 1, type((_expression_)) } -- or equivalently
The last two statements do not always add any extra value at index [2] in the table (which may then contain one or two items), but the _expression_ given to type() must still be evaluated. Can you guess a simple and reliable rule to know which dummy parentheses can be safely removed???

Instead of using extra "quirky" parentheses around the last parameter of type() to avoid the error, I think it's better to follow it by ",nil":
    table1 = { 1, type(_expression_, nil) } -- or or equivalently
    table1 = { 1, type(_expression_, nil), }

But some programmer communities may have different rules about their preferred style, and will think that appending trailing ",nil" to parameters of function calls is also quirky (or bad style) and can be safely removed (this is not the case), or that it will optimize the generated code (avoiding the extra push of a nil value on the stack) by assuming that the function never needs to explicitly have final nil arguments, except possibly in the 1st argument.

But all these are equivalent, including those with trailing ",nil":
    table2 = { 2, _expression_ }
    table2 = { 2, _expression_, } -- the trailing comma has no effect 
    table2 = { 2, (_expression_) } -- parentheses have no effect
    table2 = { 2, (_expression_), } -- idem
    table2 = { 2, _expression_, nil } -- idem
You cannot guess here how many items are in table2 (it could be 1, 2, or more). But here you you that there will not be more than 2 items:
    table3 = { 3, (_expression_), nil }
The item in table3 at [1]=3, the item at [2] is the 1st result of the _expression_ or nil, all items at [3] or higher are nil