I perfectly agree! I also have concocted some wrappers to generate LaTeX
stuff (quite frequently indeed), and the "last-arg-no-comma" thing has
been quite a recurring annoyance.
BTW, switching to {} syntax could have quite a performance hit if you
are calling that function lots of time in a tight loop. And then you
need to access the args as t[1], t[2], etc., which hampers readability,
unless you assign those to locals, and that is just additional clutter.
I also agree, It would cost nothing to allow a *single* trailing comma in function parameter lists, or in return statements, or in variable declaration lists, and their list of initializers (like for table constructors that have no limited length), even if parameter lists or return lists are (currently) bounded to a hardwired limit of 255 values (because of the
current internal limit of the VM bytecode and parts of the Lua C API); variable declaration lists or initializer lists have no such limit
Note that variable initializers lists are sensitive to the syntaxic ambiguity caused by "currified function calls" when a declaration is followed by a function call statement, so they sometimes need a semicolon after them: allowing a trailing comma for initializer lists will not change this ambiguity...
See: "local a,b,c f(x)" :no ambiguity here because c(f(x)) cannot be a valid local variable declaration
But if you allow trailing comma in declaration lists, then this could be interpreted like
"local a,b, ; c(f(x))" (with trailing comma allowed) or as "local a,b,c ; f(x)" (the only way with the current syntax)
See now:
"local a,b,c = e,f,g h(x)": no ambiguity if tralining commas are not accepted, there's a statement separation between "g" and "h"
But if you allow trailing commas in initializer lists, then thuis could be as well be interpreted either as
"local a,b,c = e,f, ; g(h(x))"
(with trailing comma allowed) or as
"local a,b,c = e,f,g(h(x))"
(the only way with the current syntax)
Such ambiguities could be solved using a semicolon. But for compatiblity with the existing syntax, it should be solved like in the current syntax:
if you really want to use trailing commas in varaible declaration lists or in initializers lists, you MUST use the additional ";" statement terminator, otherwise this syntax could be parsed with the legacy way.
This way, trailing commas are possible "without ambiguity". But if you forget the semicolon, nasty bugs could occur causing unexpected curryfied function calls (possibly generating runtime errors (trying to call a function from a value that is not a function, or calling a function but with the wrong parameter type or value...)
The same cannot happen for "return" statements as there cannot be any other function-call statement after them (ignoring whitespaces and newlines) : they can only be followed by an optional semicolon terminator, or an "elseif"/"else"/"end" token...
Note that there's probably no need to allow the comma after the "..." pseudo-_expression_ (usable only at end of the parameter list for function calls, or end of the list of values to "return", or end of the list of values in a table constructor: no other value can follow its list value. You would first need to strip down the list value to just its first member (or nil), by placing it in parentheses "(...)", so the syntax "{ 1, 2, 3, ..., }" can remain invalid, while
"{ 1, 2, 3, ... }" makes sense, which is differrent from "{ 1, 2, 3, (...) }" or
"{ 1, 2, 3, (...), }" both being valid and equivalent.
For calling functions with very long parameter lists, it is still advizable to pass a table instead: it's more flexible, and most often it will perform better (the table can be constructed only once, with few members modified between calls), and it allows more flexible programs where each parameter to pass can be computed independantly (or not be computed at all), so the limit of 255 values in function parameter lists or return is not a serious problem.
Note that function calls that have return many values are slower: Lua has to "slide down" these values on the stack after it return, to drop the call parameters (this requires an internal memcpy() in the current VM, whose time is proportional to the number of return parameters, adjusted down that what the callers wants to storeif it uses an assigment to store the results, or uses the values in an argument lsit for another function call but not as its trailing parameters); and as well it requires a larger stack size (the stack size must be at least the sum of the maximum number of input parameters and the maximum number of return values, plus enough stack stlots to compute intermediate results in expressions and performing all subcalls).
Note that this function will explode the current VM limit at runtime after 255 recursions for n=256, even if it if thas still only used 256 positions in the stack (the default stack size for any thread if much larger than 256), and could extend it to 511, but it's not possible to "compute" the call (even a trailing one) as it would have 256 parameters):
function f(n, ...)
return if n==1 then return 1 end
return n, f(n-1, ...)
end
Even if this function is recursive, it performs a trailing call by expanding the parameter list with another value: you reach the two limits of 255 values on parameter lists and on return values. The trailing call does not addup parameters lists, but parameter list (and any extra stack space still in use for local variables) are stripped down at every return or at every trailing call, to just keep the return values (but this still requires an internal memcpy() in the VM).