Skip to content

Are Variadic Functions Necessary?

February 27, 2012

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))))
(+ 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.

  1. You have to support more than lists, you actually need to support tuples. That is, each item in the list can be a distinct type. You would also have to support a default argument for this value to support a common case of an empty list. I would hate to have to write printf( “no-tokens”, [] );. Furthermore you’d need to have a run-time type system capable of deciphering those distinct types.

    Otherwise I think I’m in support of abandoning variable length parameter lists. A tuple with a default value can cover the exact same semantic needs but is likely more type-safe. There is a slight issue of efficiency, but there are ways to deal with that.

    • In dynamic languages, lists of mixed types are usually not an issue. In the case of a printf function, you need to check that the number of arguments received on the callee side is valid, but then you needed to do that with variadic functions anyways.

  2. Dimitris permalink

    Variadic functions are needed when we want to have a function that is polymorphic in the number of arguments it takes, in a way that is invisible to the programmer.

    For example, consider a bunch of functions that manipulate an AST. Most functions only do sth to particular kinds of nodes, and leave the other nodes unchanged. It’s possible to write such functions and specify only the interesting cases, and the recursion takes care of itself.

    In DoctorJS, we have a generic function that walks the JavaScript AST, called walkASTgenerator.

    At an AST node, the only thing this function does is to call itself recursively on the children of the node, passing any arguments along.

    Now, all concrete functions that manipulate the AST (such as labelAST, desugarWith, etc), start with a walkASTgenerator, and specify only the interesting cases. But these concrete functions have different arities, so walkASTgenerator must be variadic. If it took a list of arguments instead, we’d have to go to all other functions and have them take a list of things, which would make the code much harder to read.

    So, polymorphism in the number of arguments in a transparent way is the only use case I can think for variadic functions. I’m not sure how common it is, and if it alone justifies the implementation complexity of having variadic functions in a language.

  3. I was faced with this problem when I was designing my own programming language. Here is the example that convinced me that I couldn’t get by without it: Higher Order Functions.

    map(f, x, y, z)
    map is a function that takes a n-arity function f, and n additional lists, and calls f on each element in the lists.

    Consider if we didn’t have variadic or apply. How would we write a map-multiple function?
    map-multiple(f, [x, y, z])
    This requires the apply function.

    On top of that, consider how we would write a function g, that simply calls f.
    This requires variadic functions.

    If you figure out how to solve those two problems without variadic functions I would love to know. I would dearly love not to implement variadic functions in my language.


    • Very good points.

      I can see a few options:

      1. Ignore map-multiple. Decide the language can’t support it. Maybe it isn’t all that useful to begin with.

      2. Use macros to expand map-multiple at code-generation time so that it calls your function directly with its arguments without resorting to apply.

      3. Special-case map-multiple by hand up to some fixed number of arguments. This is equivalent to building some kind of fake apply that only supports up to K arguments (and has a switch statment inside to do the dispatch).

      4. Don’t have variadic functions, but still have apply. Your compiler needs to implement apply, but you potentially still win out by not having to implement the complexities of dealing with variable argument counts on the receiving end.

      • That is a very thoughtful and quick reply. After much deliberation, your 4th suggestion was what I ended up implementing. When that is combined with macros, I can recapture most of the use-cases of variadic functions. I think this is actually cleaner than Lisp/Scheme’s design, where function calls implicitly build a data-structure for you.

        Good luck with Tachyon. I am keen to see your progress.

  4. alandipert permalink

    I too have come to question variadic arguments, particularly since studying Backus’s FP language (Backus78), in which all functions take a single argument that may be either an atom (like a number) or a sequence.

    My current perspective is that unifying argument data with the language’s user-accessible data facilities reduces complexity. As a lisper, it was hard for me to accept that in light of FP, even apply feels like a kludge.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: