DIP 1019--Named Arguments Lite--Community Review Round 2

Paul Backus snarwin at gmail.com
Sun Jun 9 16:58:23 UTC 2019


On Sunday, 9 June 2019 at 02:49:13 UTC, Jonathan Marler wrote:
> I believe that optimizing code for the reader should take 
> priority over optimizing for the writer.  The antithetical 
> example is the perl language...writing it can be terse and 
> quick but reading it can make your head spin.

I agree, but forcing the programmer to put stdin before stdout is 
optimizing for neither the reader nor the writer; it's optimizing 
for the compiler developer. Both calls to spawnProcess are 
equally easy to read and write.

> Consider,
>
> foo(int a, int b, int c, int d, int e, int f);
>
> foo(1, 2, 3, 4, 5, 6); // a=1, b=2, c=3, d=4, e=5, f=6
>
> Now let's see it with some argument re-ordering:
>
> foo(6, c=1, e=2, 3, b=4, e=5, a=7);
>
> What's the values of a/b/c/d/e/f in this case? This may have 
> made sense when it was written, but this is not beneficial for 
> the reader.

In this case, you get a compile error, because you've passed 
seven arguments to a six-parameter function, and some of them are 
duplicates. :)

That aside, I think this example is only convincing because 
you've gone out of your way to choose parameter names and 
arguments that have an obvious order to them. Here's what happens 
if I replace them with names and values that don't follow any 
particular sequence:

// Declaration
double foo(double x, double t, double delta, double phi, double 
m, double tau);

// Call without names
foo(-65.22, 0.0783, 943.8, -1.7847, -7982.0, 0.8341);

// Call with names and no reordering
foo(-65.22, t=0.0783, delta=943.8, phi=-1.7847, m=-7892.0, 
0.8341);

// Call with names and reordering
foo(-65.22, delta=943.8, m=-7892.0, 0.8341, t=0.0783, 
phi=-1.7847);

To my eyes, the second and third calls look equally readable. In 
both cases, you can read off the values of four of the parameters 
from the names, you know that -65.22 is the value of the first 
parameter, and you know that 0.8341 is the value of the parameter 
that comes after `m`. In both cases, you'll have to look at the 
function signature to find out what the two unnamed parameters 
mean, unless you've memorized that `x` comes before `tau`.

Of course, the *most* readable way to call this function would be 
to use names for every argument--and then it wouldn't matter at 
all what order they were in.

> This gets worse when you introduce the concept of 
> order-of-evaluation:
>
> int x = 0;
> foo(x++, c=x++, e=x++, x++, b=x++, e=x++, a=x++);
>
> Now what are the values of a/b/c/d/e/f?  Do you evaluate 
> arguments in the order of the call, or the order of the 
> function definition?

Personally, I think the obviously-correct choice is to evaluate 
them in lexical order (i.e., order of the call). Either way, 
though, this is something people will have to look up in the 
language spec the first time they see it, so it does make the 
language a little more difficult to learn.

Of course, for this specific example, the real answer is "don't 
write code like that."

> These are the problems before you get into overloading.  Now 
> add that into the mix:
>
> foo(float a, float b, float c, int   d, float e, float f);
> foo(float a, float b, float c, float d, float e, int f);
>
> foo(0, c=1, 2.0, 0.0, b=4, e=5, a=7);
>
> ...
>
> [...] Now whenever they see a function call with named 
> arguments, they're going to have to go through a mental 
> checklist of rules to be sure which overload is actually being 
> called and which parameters are receiving which arguments.

I think if you write overload sets like this, someone reading 
your code is going to have a bad time regardless of whether you 
use reordering or not. Compare:

foo(0, c=1, 2.0, 0.0, b=4, f=5); // with reordering
foo(0, b=4, c=1, 2.0, 0.0, f=5); // without reordering

In both cases, you have to find `d` and `f` in the argument list 
to figure out which overload is called. In both calls, `f` is 
named, so it's easy to find, and `d` is passed positionally, so 
you have to find it by looking for `c` and counting one argument 
to the right.

Of course, an even more readable way to call this function would 
be to pass both `d` and `f` as named arguments--and if you did 
that, the order you passed them in wouldn't matter.

All in all, the conclusion I draw from this discussion is that 
allowing argument reordering makes good code easier to write 
without harming readability, at the expense of potentially making 
bad code less readable. That seems to me like an acceptable 
tradeoff, especially since programmers are unlikely in practice 
to use named arguments in ways that don't improve the readability 
of their code.


More information about the Digitalmars-d mailing list