June 09, 2019
On Sunday, 9 June 2019 at 11:54:25 UTC, Atila Neves wrote:
> On Sunday, 9 June 2019 at 07:55:50 UTC, Andrei Alexandrescu wrote:
>> On 6/8/19 3:58 PM, Yuxuan Shui wrote:
>>> 
>>> Reordering is definitely a very important feature. However, this proposal is not going to be the last DIP about named parameters, as is stressed in the proposal itself.
>>> 
>>> Generally, I think incremental improvements should be allowed.
>>
>> Sadly my vast and mostly unsuccessful experience with programming language design is that such is not to be done incrementally. Never. I will oppose a DIP that argues its utility by means of possible future DIPs. (For measure, don't forget I do not hold decision power any longer.) For named arguments, I very strongly believe that any DIP that does not allow reordering is a stillborn.
>
> I agree. Reordering is the main motivation. I've lost count of how many times I've shaken my fist at the sky when trying to change the working directory when calling std.process.execute.

I'm curious what the dev process is here? You write a function, start filling in it's parameters and you get it wrong. Ok, so you check the docs right? And then you fill in the arguments while looking at the docs right? Or?

Also do you think you'll have a similar experience with reorderable named parameters where instead of the position it is now the name of the argument? And then on top of that, if we allow non-named parameters to mix with named parameters, then what does reordering give you?
June 09, 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.
June 09, 2019
On Sunday, 9 June 2019 at 13:39:34 UTC, aliak wrote:
> On Sunday, 9 June 2019 at 11:54:25 UTC, Atila Neves wrote:
>> On Sunday, 9 June 2019 at 07:55:50 UTC, Andrei Alexandrescu wrote:
>>> On 6/8/19 3:58 PM, Yuxuan Shui wrote:
>>>> 
>>>> Reordering is definitely a very important feature. However, this proposal is not going to be the last DIP about named parameters, as is stressed in the proposal itself.
>>>> 
>>>> Generally, I think incremental improvements should be allowed.
>>>
>>> Sadly my vast and mostly unsuccessful experience with programming language design is that such is not to be done incrementally. Never. I will oppose a DIP that argues its utility by means of possible future DIPs. (For measure, don't forget I do not hold decision power any longer.) For named arguments, I very strongly believe that any DIP that does not allow reordering is a stillborn.
>>
>> I agree. Reordering is the main motivation. I've lost count of how many times I've shaken my fist at the sky when trying to change the working directory when calling std.process.execute.
>
> I'm curious what the dev process is here? You write a function, start filling in it's parameters and you get it wrong. Ok, so you check the docs right?

Most times the compiler complains.

> And then you fill in the arguments while looking at the docs right? Or?

I fix the call based on the compiler error. I may or may not look at the function declaration.

> Also do you think you'll have a similar experience with reorderable named parameters where instead of the position it is now the name of the argument?

The hypothetical isn't needed, I already program that way when I can:

https://github.com/atilaneves/kwargs

Telling the compiler which argument matches each parameter with a type name or variable name seems, to me, to be a minor distinction. They're both symbols.

> And then on top of that, if we allow non-named parameters to mix with named parameters, then what does reordering give you?

I thought I already covered that with my std.process.execute example. Right now I have to do this:

auto myExecute(string[] args, string workDir) {
    import std.process: Config, execute;

    const string[string] env;
    const config = Config.none;
    size_t maxOutput = size_t.max;

    return execute(args, env, config, maxOutput, workDir);
}

When really I just want to:

import std.process: execute;
execute(args, WorkDir("~/foo/bar")); // or workdir: "~/foo/bar", or whatever

I've typed the former more times than I can count, despite never having cared about changing the default values of the intermediate arguments.

June 09, 2019
On Sunday, 9 June 2019 at 17:04:12 UTC, Atila Neves wrote:
> On Sunday, 9 June 2019 at 13:39:34 UTC, aliak wrote:
>> [...]
>
> Most times the compiler complains.
>
>> [...]
>
> I fix the call based on the compiler error. I may or may not look at the function declaration.
>
> [...]

You can have skip default behavior with unorderable as well.
June 09, 2019
On Sunday, 9 June 2019 at 11:16:30 UTC, aliak wrote:
> On Sunday, 9 June 2019 at 01:34:50 UTC, Paul Backus wrote:
>>
>> Without looking at the documentation, which of the following calls would fail to compile if argument reordering was forbidden?
>>
>> spawnProcess("/usr/bin/sort", stdout: outputFile, stdin: inputFile);
>> spawnProcess("/usr/bin/sort", stdin: inputFile, stdout: outputFile);
>>
>> From a usability perspective, it's completely insane for only one of these to work.
>
> Without looking at the documentation, what are the parameters of this function
>
> blah(...)
>
> ?

My point is not that programmers should be able to write code without ever looking at documentation, it's that programmers should not have to do mindless busywork that the compiler could just as easily do for them.
June 09, 2019
On 6/9/19 7:15 AM, aliak wrote:
> On Sunday, 9 June 2019 at 07:51:18 UTC, Andrei Alexandrescu wrote:
>> On 6/8/19 2:05 PM, Jonathan Marler wrote:
>>> I see value in allowing a caller to omit arguments that have default values, but what is the value in allowing the caller to reorder them?
>>
>> Named arguments is useful mainly so one doesn't need to remember their order in large argument lists.
> 
> When has anyone had a problem with unreorderable named arguments in functions with large argument lists?

This is a leading question. When has anyone hadn't?

> After using languages with unreorderable and reorderable named parameters. I only see a very mild benefit of programmer laziness for reorderable, and the larger downside of reader annoyance. I've felt no downsides of unreorderable (you generally look at the documentation, and type it in param by param, or copy paste the declaration, and if you get it wrong, the compiler tells you and you fix it).

What would be those languages? The Wikipedia page https://en.wikipedia.org/wiki/Named_parameter mentions:

"With named parameters, it is usually possible to provide the values in any arbitrary order, since the name attached to each value identifies its purpose. This reduces the connascence between parts of the program. A few languages use named parameters but still require the parameters to be provided in a specific order."

So it looks like reordering is prevalently supported. The page https://rosettacode.org/wiki/Named_parameters mentions that the following languages do not support reordering (upon a quick look): Elixir, Fortran, Nemerle.

Is this the company we want to be in? From what I see in a few popular languages:

* Python: reordering is a feature. From https://treyhunner.com/2018/04/keyword-arguments-in-python: "When we use keyword/named arguments, it’s the name that matters, not the position"

* C# 4: reordering is a feature. From https://en.wikipedia.org/wiki/C_Sharp_4.0#Optional_parameters_and_named_arguments: "Parameter names can be specified for both optional and required parameters, and can be used to improve readability or arbitrarily to reorder arguments in a call."

* Ruby: reordering is a feature. From https://thoughtbot.com/blog/ruby-2-keyword-arguments: "Keyword arguments allow us to switch the order of the arguments, without affecting the behavior of the method"

* Scala: reordering is a feature. From https://docs.scala-lang.org/tour/named-arguments.html: "Notice how the order of named arguments can be rearranged."

That's why the "Related Work" section is crucial. It needs to make a careful account of experience in existing languages. For my money, named arguments with no reordering means we're wasting our time.
June 09, 2019
On 6/9/19 7:27 AM, aliak wrote:
> With reorderable, as Walter pointed out, what't the order (parameter-wise or argument-wise).

Lexical order of course.
June 09, 2019
On 6/9/19 9:39 AM, aliak wrote:
> On Sunday, 9 June 2019 at 11:54:25 UTC, Atila Neves wrote:
>> On Sunday, 9 June 2019 at 07:55:50 UTC, Andrei Alexandrescu wrote:
>>> On 6/8/19 3:58 PM, Yuxuan Shui wrote:
>>>>
>>>> Reordering is definitely a very important feature. However, this proposal is not going to be the last DIP about named parameters, as is stressed in the proposal itself.
>>>>
>>>> Generally, I think incremental improvements should be allowed.
>>>
>>> Sadly my vast and mostly unsuccessful experience with programming language design is that such is not to be done incrementally. Never. I will oppose a DIP that argues its utility by means of possible future DIPs. (For measure, don't forget I do not hold decision power any longer.) For named arguments, I very strongly believe that any DIP that does not allow reordering is a stillborn.
>>
>> I agree. Reordering is the main motivation. I've lost count of how many times I've shaken my fist at the sky when trying to change the working directory when calling std.process.execute.
> 
> I'm curious what the dev process is here? You write a function, start filling in it's parameters and you get it wrong. Ok, so you check the docs right? And then you fill in the arguments while looking at the docs right? Or?
> 
> Also do you think you'll have a similar experience with reorderable named parameters where instead of the position it is now the name of the argument? And then on top of that, if we allow non-named parameters to mix with named parameters, then what does reordering give you?

A missing straitjacket.
June 09, 2019
On Sunday, June 9, 2019 12:40:21 PM MDT Paul Backus via Digitalmars-d wrote:
> On Sunday, 9 June 2019 at 11:16:30 UTC, aliak wrote:
> > On Sunday, 9 June 2019 at 01:34:50 UTC, Paul Backus wrote:
> >> Without looking at the documentation, which of the following calls would fail to compile if argument reordering was forbidden?
> >>
> >> spawnProcess("/usr/bin/sort", stdout: outputFile, stdin:
> >> inputFile);
> >> spawnProcess("/usr/bin/sort", stdin: inputFile, stdout:
> >> outputFile);
> >>
> >> From a usability perspective, it's completely insane for only one of these to work.
> >
> > Without looking at the documentation, what are the parameters of this function
> >
> > blah(...)
> >
> > ?
>
> My point is not that programmers should be able to write code without ever looking at documentation, it's that programmers should not have to do mindless busywork that the compiler could just as easily do for them.

If programmers don't read the documenation, then they're just begging for trouble. It shouldn't be necessary to read the documentation every time you use a function or other symbol from a library, but it's a serious problem if programmers think that they can get away with never reading the documentation to see how the code they're using is even supposed to work.

- Jonathan M Davis



June 09, 2019
On Sunday, 9 June 2019 at 19:43:54 UTC, Andrei Alexandrescu wrote:
> On 6/9/19 7:27 AM, aliak wrote:
>> With reorderable, as Walter pointed out, what't the order (parameter-wise or argument-wise).
>
> Lexical order of course.

Lexical order of the definition or the caller?

So far we have 1 for the caller and 1 for the definition. Both seem to think their choice is the obviously correct one :)

  > Patrick Schluter:
  > They are in the order of declaration of the function...There's no reason to evaluate it any other order as the code generation requires that the caller populates the registers/stack in the order the function expects.

  > Paul Backus:
  > Personally, I think the obviously-correct choice is to evaluate them in lexical order (i.e., order of the call).