Are Variadic Functions Necessary?

Many, if not most modern programming languages offer variadic functions, that is, functions that can accept a variable (indefinite) number of arguments. Perhaps the oldest language to offer this feature was LISP. The way dynamic languages such as LISP, Python and JavaScript typically implement this is by making it possible for functions to access their arguments in the form of a list of values. The caller can pass as many arguments as desired, and the callee receives the argument values in a list which can have variable length.

In most LISP derivatives, variadic functions occupy a central position in the language. This is made clear by the fact that most LISP primitive operators (e.g.: +, -, *, /, and, or, etc.) can apply to a variable number of arguments. LISP languages also typically offer a special apply function which is used to pass a list of arguments to a given function. It's understandable that LISP languages would want these operators to be variadic, as the syntax could quickly become tiresome otherwise if the basic operators could only accept two arguments:

(+ a (+ b ( + c (+ d e)))) vs (+ a b c d e)

What about other languages? Do they really need variadic functions? Are variadic functions even that useful? From a language design perspective, I will argue that this is not the case. If we take JavaScript, for example, it's fairly common to see definitions of functions which require at least n arguments, but can take extra arguments. One could imagine a JavaScript printf function, for example, which takes a mandatory format string, and optional values to insert into the formatted string:

printf("I have %i apples, %i lemons and %i oranges", v1, v2, v3);

There is an easy alternative, however. One could simply pass the value arguments in a list:

printf("I have %i apples, %i lemons and %i oranges", [v1, v2, v3]);

This might actually be better, as it makes it obvious that we are passing a list of value arguments that is semantically distinct from the mandatory format string. Using a variadic functions, the list was implicit, and there was no syntactic indication that a list of values was being passed. Hence, passing an explicit list of arguments makes the function's usage and the programmer's intention clearer.

I argue that in JavaScript, all cases where variadic functions are used could be adequately served with lists of arguments. The main disadvantage (if it really is one) is that the programmer is required to type two extra characters (the list delimiters). If one designed a language with this in mind, though, syntactic sugar could be provided to mean that remaining arguments after a certain point are to be passed as a list:

printf("I have %i apples, %i lemons and %i oranges" ; v1, v2, v3)

At this point, you might wonder why one would want to design a programming language without variadic functions. One reason is that they are superfluous, and it might be best, in the spirit of clarity and simplicity, not to include them. Another is that removing variadic functions would help simplify the implementation of the said language. JavaScript VMs, for example, must implement more complex calling conventions to support the efficient passing of a variable number of arguments and the apply function.

In addition to this, JavaScript provides the arguments object primarily to support variadic functions. The arguments object makes optimization of JavaScript programs more difficult and its efficient implementation significantly complexifies JavaScript VMs. Much of this complexity could be avoided in a language which did not provide variadic functions and instead simply encouraged passing explicit lists of arguments when desired.

I believe that most languages, especially the ones that feature native support for lists or arrays, could do away with variadic functions. LISP derivatives could too, if their primitive operators were implemented in terms of variadic macros that unfold into applications of binary primitive operators. That is, it would be possible to design a LISP language in which functions are not variadic, but macros can be.