Function signature: how do you order parameters?

Most programming languages force you to order your function parameters. Getting them wrong might break your code.

What is the most natural way to order the parameters?

A language should aim to be generally consistent to minimize surprises. For example, in most languages, to copy the value of the variable source into variable destination, you’d write:

destination = source;

So it makes sense that, in C, we have the following copy function:

memcpy(void *destination, const void *source, size_t n);

Go, the new language designed by some of the early C programmers, follows the same tradition:

func copy(destination, source []T) int

Meanwhile, Java is arguably backward with the arraycopy function:

void arraycopy(Object source,
             int sourcePos,
             Object destination,
             int destinationPos,
             int length);

The justification for Java’s order is that, in English, you say that you copy from the source to the destination. Nobody says “I copied to x the value y“. But then why don’t we write a value copy from y to x as follows?

y->x;

Sadly, Java is not even consistent in being backward. For example, to copy data from the ByteBuffer destination to the ByteBuffer source, you have to write:

destination.put(source);

This is, in my opinion, the correct order, but if you are used to having the source being first, you might be confused by that particular ordering.

Ok. So what about working with a data structure, like a hash table? A hash table is not very different from an array conceptually, and we set array values in this manner:

array[index] = value

So I would argue that the proper function signature for the insertion of a key-value in a hash table should be something of the sort:

insert(hashtable_type hashtable, 
        const key_type key, 
        const value_type value);

In Go, this is how you add an element to a Heap:

func Push(h Interface, x interface{})

More generally, when acting on a data structure, I would argue that the data structure being modified (if any) should be the first parameter.

What if you want to implement an in-place operation, for example, maybe you want to compute the bitwise AND of x and y, and put the result in x:

x &= y

And this how you’d implement it in C++, putting the x before the y:

type operator&(type & x, const type& y) {
   return x &= y;
}

I wonder whether it would be possible to produce a tool that detects confusing or inconsistent parameter ordering?

11 thoughts on “Function signature: how do you order parameters?”

  1. I could be wrong, but I think in C and C++ we pass variable parameters, then const parameters, then optional parameters.

    Some friendly languages allow us to re-order parameters in the call, if we use something like CopyObject(source = s, dest = d, len = l).

    1. I could be wrong, but I think in C and C++ we pass variable parameters, then const parameters, then optional parameters.

      Here is the fwrite function signature: fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);.

      Some friendly languages allow us to re-order parameters in the call, if we use something like CopyObject(source = s, dest = d, len = l).

      As wikipedia points out, it is even possible in C if you pass a struct:

      https://en.wikipedia.org/wiki/Named_parameter

  2. Personally, my “trumps everything else” rule is: order parameters in such a way that parameters of the same (or same family, such as uint8_t and uint32_t) are well separated so there’s no tendency to confuse them in a way that still compiles. The classic example of how NOT to do an API is memset: it takes essentially [if you unpick the char & size_t typedefs) (void*, uint8_t, uint32_t) with the second parameter being the one to set the bytes to while the second is the size, but in the wild you still see code that has very clearly got it the wrong way around, yet somehow hasn’t been detected yet. Given that I always follow an “array ptr” immediately by the length, I’d choose to structure memset as (uint8_t valueToSetTo, void* ptr, size_t length).

  3. `Nobody says “I copied to x the value y“.’

    Though we might say “I made an x with the value of y”, or “I made an x by copying y”.

  4. John Lakos specifies something similar in “Large Scale C++” from 1997 – arguments being modified come first in the parameter list. He also specifies they should be pointers so that it is obvious at the calling site they will be modified – this makes them different to the const-references elsewhere. not as fashionable these days but still a convention we follow.

    (Your C++ example should be “type& x”)

    1. That works on the writing side. The ogher issue is reading call sites (where obviously the variable names may be be based on their complete usage and not just what it means in this function call). I suppose you do a mouseover tooltip.

  5. i can’t talk for C / C++ etc, but in JS the order or params can become really important if your trying to use a functional programming approach.

    eg. `copy(from, to) ` may read well, but when using bind because one of those params is “constant” for your program `copyThing = copy.bind(copy, thing);` i would take a lot of time to try and choose the common case and arrange params in the order that it would make sense to bind them.

  6. “More generally, when acting on a data structure, I would argue that the data structure being modified (if any) should be the first parameter.”
    In functional programming that has partial calls and composition, this is the opposite. To build up a more complex operation, you compose functions together, leaving the last argument to be filled by the result of the previous function in the chain. The last thing passed in the thing being acted upon (at least by the first function in the chain), which is often the data structure.

    In an OO world, you’d be correct, though.

Leave a Reply

Your email address will not be published. Required fields are marked *