April 02, 2019
On 02/04/2019 8:03 AM, Andrei Alexandrescu wrote:
> 
> At any rate, this is unlikely to win a beauty contest:
> 
> void fun(<bool a=b>c>);

Indeed. Hence I'm open to alternatives if we can find a good one. Its a hard syntax to come up with while keeping it consistent with all the current semantics.

> Also, it seems named parameters without reordering is like a wedding without music.

We have three schools of thought in the D community. In around equal shares is those who think reordering is bad, and another who think its great. Finally there is also those who don't care much. Which I think take the larger portion.

I'm trying to find a balance between the three. None of the schools of thought seem to win out. They each have their pros and cons. But I'm in the belief that I have struck a good middle ground that gives us the chance later on to either restrict or loosen once we have gotten some experience with it.
April 02, 2019
On 02/04/2019 12:22 PM, sarn wrote:
> On Monday, 1 April 2019 at 14:41:20 UTC, rikki cattermole wrote:
>> On 02/04/2019 3:09 AM, Atila Neves wrote:
>>> There are known reasons why using angle brackets complicates lexing (cough! C++! cough!), so the syntax choice is odd. Is there a particular reason why the usage suggested here avoids the issues?
>>
>> As far as I'm aware, we should have no problems with adapting dmd-fe to support it.
>>
>> Of course I could have missed something (either in dmd or outside). So it would be good to have somebody else double check that statement.
> 
> You already have to deal with the case of variadic function definitions, so why not just extend the syntax?
> 
> I.e., in this declaration
> 
> void foo(int a, int b, ..., int c);
> 
> c must be a named argument, even without any special decorators. In that case the ... argument acts as a delimiter.  So all you need is rules for separating positional arguments from named arguments for non-variadic functions.

Looks like a good idea, although a poor choice for this DIP.
It could only work for function parameters, it won't work for templates.

I don't think I've seen this idea come up before, so please do explore it if we don't get another solution in before hand!
April 02, 2019
On Tuesday, 2 April 2019 at 00:49:08 UTC, rikki cattermole wrote:
> On 02/04/2019 12:22 PM, sarn wrote:
>> On Monday, 1 April 2019 at 14:41:20 UTC, rikki cattermole wrote:
>>> On 02/04/2019 3:09 AM, Atila Neves wrote:
>>>> There are known reasons why using angle brackets complicates lexing (cough! C++! cough!), so the syntax choice is odd. Is there a particular reason why the usage suggested here avoids the issues?
>>>
>>> As far as I'm aware, we should have no problems with adapting dmd-fe to support it.
>>>
>>> Of course I could have missed something (either in dmd or outside). So it would be good to have somebody else double check that statement.
>> 
>> You already have to deal with the case of variadic function definitions, so why not just extend the syntax?
>> 
>> I.e., in this declaration
>> 
>> void foo(int a, int b, ..., int c);
>> 
>> c must be a named argument, even without any special decorators. In that case the ... argument acts as a delimiter.  So all you need is rules for separating positional arguments from named arguments for non-variadic functions.
>
> Looks like a good idea, although a poor choice for this DIP.
> It could only work for function parameters, it won't work for templates.
>
> I don't think I've seen this idea come up before, so please do explore it if we don't get another solution in before hand!

Is it possible to lex the "public" keyword unambiguously when applied to parameters?

long foo(int v, int w, int x, public int y, public int z)

foo(1, 2, 3, y: 4, z:5);

Many named args proposals for Rust propose this syntax. Another alternative is `export` on parameters.

Or, something a little more interesting, but with unknown ramifications and possibly insane:

//z is a named parameter with a default value
long foo(int v, int w, int x, struct optargs { int y, int z = 3 })

foo(1, 2, 3, { y: 4 });
foo(1, 2, 3, { y: 4, z: 5 });
foo(1, 2, 3, optargs { y: 4, z: 5 });
foo(1, 2, 3, optargs { 4, 5 });
foo(1, 2, 3, { z: 5 }); //Error: cannot call function foo(int, int, int, struct optargs { int y, int z = 3 }) with arguments (1, 2, 3, { z: 5 }). Missing non-optional parameter y in optargs

Just channeling the old struct DIP. I have no idea if this can even be lexed unambiguously.


April 03, 2019
On Sunday, 31 March 2019 at 12:33:56 UTC, Mike Parker wrote:
> This is the feedback thread for the first round of Community Review for DIP 1020, "Named Parameters":
>
> https://github.com/dlang/DIPs/blob/39dbbbe5e4618abd4c4b41eb0edd16547858ddf5/DIPs/DIP1020.md
>
> All review-related feedback on and discussion of the DIP should occur in this thread. The review period will end at 11:59 PM ET on April 14, or when I make a post declaring it complete.
>
> At the end of Round 1, if further review is deemed necessary, the DIP will be scheduled for another round of Community Review. Otherwise, it will be queued for the Final Review and Formal Assessment by the language maintainers.
>
> Please familiarize yourself with the documentation for the Community Review before participating.
>
> https://github.com/dlang/DIPs/blob/master/PROCEDURE.md#community-review
>
> Thanks in advance to all who participate.

I think this must go to formal assessment together with DIP1019 (named arguments lite). In fact, these two DIPs are close enough that you could consider merging them together.

What I prefer about DIP1019 is the syntax, and the fact that you can reorder arguments. However, I'd like to keep the ability from this DIP to combine unnamed parameters with named ones.

I propose that you let the caller to reorder named parameters freely. It should not cause complications, because they do not affect the overloading resolution anyway. Internally, the compiler could order named parameters alphabetically. Add that to a better syntax, and it starts to sound tempting.
April 05, 2019
This is my follow up reply, with my responses to common questions and points raised in this review as well as an updated copy of my actionable points list.

It took slightly longer than I expected, so apologies for that.

TLDR: I will be adding an alternative syntax as well as options for full and no reordering of arguments to the DIP following this review.

The current (final?) list of actionable points is as follows:

- Angle brackets, may be hard to find given relationship with templates
  Potential solutions include ``@named``
- External access of template parameters externally needs justification
- Interaction with eponymous templates, invalid
- "Future proposals" sections should probably have a full rewrite to indicate its for recognizing desirable semantics that are out of scope.
- Not in competition with in place struct initialization, different purposes
- More headings (break down behavior)
- Reorder of arguments does not change at symbol site, only at the argument side
- Overload resolution code is flawed with illegal code
- Compare and contrast example code
- Logging case demonstrates that for functions unnamed should be the default API wise
- Duplicate naming is bad, flagging of a parameter as named is better
- Partial reordering needs examples!
- Angle bracket syntax will have problems if the parser doesn't peek one token ahead of >.
- Partial reordering why?

The list of questions I have answered is:

- Angle brackets, may be hard to find given relationship with templates
- External access of template parameters externally needs justification
- Interaction with eponymous templates, invalid
- Not in competition with in place struct initialization, different purposes
- Reorder of arguments does not change at symbol site, only at the argument side
- Partial reordering why?

If there is a question that I didn't end up answering, please do give me a ping, I did intend to have given a response to everything at this point in time.

A copy of the responses and the above list can be found here: https://gist.github.com/rikkimax/e9bee9fd1704aab1dcb1dafc26d3207d

As always, if I have forgotten something please let me know!










# Angle brackets, may be hard to find given relationship with templates

Agreed.

The angle bracket syntax was chosen in the current iteration of the DIP because it satisfies these criteria:

1. For a type on template parameters without the curved brackets (the only reason I indulged this requirement is because of the keyword)
2. As template parameters
3. As function parameters
4. Opt-in, easy to convert to and from
5. Consistent between all the above points

Alternative designs including the usage of ``@named`` attribute is desirable, but require further research.

So far any attempt to satisfy these requirements has failed.
All other characters that have both an open and a close pair on regular keyboards are already in use by D in respect to types.
Overloading their usage further in the same design space would cause more harm than good.

The first point is a feature I want, which cannot be meet in using the ``@named`` attribute design. Alas, perfect is the enemy of good, which means I'm going to have to compromise and remove it for other potential designs.

After some research into dmd.parse.d, the angle bracket syntax will require peeking into the next token when finding a '>' character as part of expression parsing. If the next token is valid for an expression instead of comma or closed bracket, then expression else end of named parameter definition. This complication makes this syntax less than desirable although not a blocker. Which confirms that yes alternative designs certainly should be considered.

# External access of template parameters externally needs justification

After further research, I can confirm that I cannot find any examples of named parameters being used as a first class citizens of meta-programming similar to what D has. This means that any work towards named template parameters must evolve from what we have already got.

Template parameters whose name has not been specified as part of their argument list demonstrate that their purpose is internal to the type and results in minimal concern to the initializer. Not all cases will appear this way. For example a type specified as the first argument on a data structure would imply that it is the type of the data being stored. But a boolean following it to enable GC interactions does not explain what it is there for hence why std.typecons.Flag exists.

For best effect a named template parameter is a subset of template parameters that the initializer has specifically set beyond the standard unnamed set. Because the initializer specifically requested it (or could in the future), it must therefore care about its value even if it has not been set. Being able to access this value without the usage of the is expression to extract it, demonstrates that it is part of the behavior that the type exhibits.

The above conclusions are the basis for my decision to expose a named template parameter as a member.

# Interaction with eponymous templates, invalid

The current logic is that if a template parameter name matches the template's, it will not act as a eponymous template. This should extend to named template parameters as well.

The spec makes it clear that template parameters cannot make an eponymous template.

"If a template contains members whose name is the same as the template identifier": yes, a named template parameter is visible as a member, but it should not be a member in the AST
"and if the type or the parameters type of these members include at least all the template parameters then these members are assumed to be referred to in a template instantiation:": potentially no

The semantics are uncharged by the DIP. I.e. named template parameters should not appear in a __traits(allMembers, T) request.

# Not in competition with in place struct initialization, different purposes

The facilitation of passing of arguments to a given function/template is done by either a named or unnamed parameter. In place struct initialization provides domain objects a way to be easily constructed and passed via one of the parameters. Individually an in place struct initialization syntax would be capable of emulating named arguments if you are willing to access and define a struct which is the parameter list.

Consider:

```d
void myFunction(Rect, <bool myFlag=false>) {}
myFunction({x: 1, y: 1, width: 9, height: 9}, myFlag: true);
```

In the function call the position and size is grouped as a domain object keeping the values together. Without in place struct initialization they will be separate in the arguments list and could potentially be not together with full reordering enabled. Which is poor programming.
The flag could be an unnamed argument as well. But then you couldn't put it at the start or move it around in the arguments list as appropriate.
If the flag, size and position arguments are stored in a struct, without using multiple layers of in place struct initialization you would not be keeping the domain objects together.

These two language features do not subtract from each other. They can be used to complement, with a well designed API.

# Reorder of arguments does not change at symbol site, only at the argument side

When a function with named parameters is compiled, its parameter order is fixed. The ordering in the argument list will be altered to match the parameters of the function. It does not matter what order the caller chose as long as it is valid.

The exact order of the arguments being passed to a function has not been defined as of yet. But because this is an extern(D) only feature, what is chosen should not be of concern to non-compiler developers and can be changed freely without error. The same can be said about template named+unnamed parameter order.

# Partial reordering why?

There are three philosophies of thought about ordering of named arguments that I have found so far.

1. Full
2. None
3. Some form of partial

Full and none have very vocal groups in the D programming language community. Each philosophy has its advantages and disadvantages. With many a bad code written and abused of this feature in either camp. The 'default' option this DIP will offer is a partial reordering that does not restrict to the placement of the start of the named arguments or where the unnamed arguments end. The strong requirement of order between the named arguments that must match relatively to the parameters that they match to, exists to prevent full reordering. It is a sane default but does have the weakness that the order the parameters are defined it may not be suitable for the user.

If at some point the option desired changes, the change could be very breaking or it could be minimal in damages:

- To convert partial to full would require removing of code in a frontend. No breakage.
- To convert partial to none would require adding of code in the frontend. Breakage but it could be a deprecation warning followed by an error.
- To convert full to none would require adding of code in the frontend with significant breakage.
- To convert none to full would require removing of code in the frontend with no breakage.

For functions full is a clear winner for argument reordering, giving minimal restrictions and maximum use cases. But for template parameters it is not so clear cut. As per "External access of template parameters externally needs justification" we do not have the evidence to support what behavior is desirable at this point in time. Being conservative at the current time seems to be best way forward until we have experience with this potential language feature.
April 05, 2019
On Sunday, 31 March 2019 at 12:33:56 UTC, Mike Parker wrote:
> This is the feedback thread for the first round of Community Review for DIP 1020, "Named Parameters":
>
> https://github.com/dlang/DIPs/blob/39dbbbe5e4618abd4c4b41eb0edd16547858ddf5/DIPs/DIP1020.md
>
> All review-related feedback on and discussion of the DIP should occur in this thread. The review period will end at 11:59 PM ET on April 14, or when I make a post declaring it complete.
>
> At the end of Round 1, if further review is deemed necessary, the DIP will be scheduled for another round of Community Review. Otherwise, it will be queued for the Final Review and Formal Assessment by the language maintainers.
>
> Please familiarize yourself with the documentation for the Community Review before participating.
>
> https://github.com/dlang/DIPs/blob/master/PROCEDURE.md#community-review
>
> Thanks in advance to all who participate.

There are a couple of versions a parameter in a function can be syntactically appear:

void f(int param = 0); // named with default value
void f(int param); // named without default value
void f(int = 0); // unnamed with default value
void f(int); // unnamed without default value

Named arguments must be compatible with at least the named variants. It must not be disturbed by the others.

A bigger issue are template arguments -- here the variety is even bigger:

template t(T) { }
template t(T : S) { } // *
template t(T = int) { }
template t(T : S = int) { }

plus any value parameter (see above)

alias parameters probably can be handled as if `alias` were the type in the value parameter case.

The starred one indicates that the colon is a nontrivial option for named arguments. First, it must not interfere with the type-colon, and it must be clear enough for programmers reading the code.

I strongly believe that the most viable solution is a new token := which should be used at caller and/or callee site. Like:

void f(int param = 0 := arg);
f(arg := value);

It's obviously compatible with anything and reads nicely. In fact, Visual Basic uses this at call site.

1. Should the callee have to specify argument names to make calling with named arguments possible? I don't know.
2. Should multiple namings be possible, i.e. overload by argument name? No. Name the function differently.
3. Should reordering be possible? If ever, only if *all* parameters are named and naming at call site necessary.
4. Should we wait until struct initialization? Yes. It can solve much of that.

April 05, 2019
On Sunday, 31 March 2019 at 15:53:53 UTC, Andre Pany wrote:
> On Sunday, 31 March 2019 at 12:33:56 UTC, Mike Parker wrote:
>> This is the feedback thread for the first round of Community Review for DIP 1020, "Named Parameters":
>>
>> https://github.com/dlang/DIPs/blob/39dbbbe5e4618abd4c4b41eb0edd16547858ddf5/DIPs/DIP1020.md
>>
[..]
>
> I still have the opinion we should go for in place struct initialization first. Here we get named parameters implicit without adding a complete new syntax. I also do not like the new syntax <>.
> Leveraging what inplace struct initialization syntax gives us, seems a lot more reasonable in my opinion.
>
> Kind regards
> Andre
Upvote +1!
please no: <> !

April 05, 2019
On Friday, 5 April 2019 at 00:17:02 UTC, Q. Schroll wrote:
> On Sunday, 31 March 2019 at 12:33:56 UTC, Mike Parker wrote:
>> [...]
>
> There are a couple of versions a parameter in a function can be syntactically appear:
>
> [...]

If we really want to go for a new named parameter syntax your syntax proposal looks the most reasonable so far.

1  no
2. I do not know
3. Yes. I would add following rule: after the first named argument, only named arguments can follow
4. Yes

Still thinking the struct initialization dip will cover 80% of the named parameter use case.

Kind regards
Andre

April 06, 2019
On 3/31/2019 5:33 AM, Mike Parker wrote:
> This is the feedback thread for the first round of Community Review for DIP 1020, "Named Parameters":

Here's a much simpler proposal, based on the recognition that D already has named parameters:

  https://dlang.org/spec/struct.html#static_struct_init

and related:

  https://dlang.org/spec/hash-map.html#static_initialization
  https://dlang.org/spec/arrays.html#static-init-static

The only additional syntax would be allowing the equivalent of https://dlang.org/spec/hash-map.html#static_initialization in the argument list. Assignment of arguments to parameters would work the same way as assignments to fields.

As for the semantics, given this constraint, nothing needs to be invented, just discovered as necessary behavior. Consider:

   void snoopy(T t, int i, S s);     // A
   void snoopy(S s, int i = 0; T t); // B

and calling it:

   S s; T t; int i;
   snoopy(t, i, s); // A
   snoopy(s, i, t); // B
   snoopy(s, t); // error, neither A nor B match
   snoopy(t, s); // error, neither A nor B match
   snoopy(s:s, t:t, i:i); // error, ambiguous
   snoopy(s:s, t:t); // B
   snoopy(t:t, s:s); // B
   snoopy(t:t, i, s:s); // A
   snoopy(s:s, t:t, i); // A

I.e. function resolution is done by constructing an argument list separately for each function before testing it for matching. Of course, if the parameter name(s) don't match, the function doesn't match. If a parameter has no corresponding argument, and no default value, then the function doesn't match.

I don't see much point in requiring the names, preventing use of names, nor aliasing them. This has never been an issue for struct initializers.

One nice thing about this is the { } struct initialization syntax can be deprecated, as S(a:1, b:2) can replace it, and would be interchangeable with constructor syntax, making for a nice unification. (Like was done for array initialization.)

One issue would be order of evaluation: would the arguments be evaluated in the lexical order of the arguments, or the lexical order of the parameters?

Rationale:

Although this supports reordering, the real reason for naming is so one can have a function with a longish list of parameters, each with a reasonable default, and the user need only supply the arguments that matter for his use case. This is much more flexible than the current method of putting all the defaults at the end of the parameter list, and defaulting one means all the rest get defaulted.

A secondary reason is (again) for a longish list of parameters, when the user finds himself counting parameters to ensure they line up, then named parameters can be most helpful.
April 07, 2019
That solves function arguments, what is your proposal for templates?