DIP 1020--Named Parameters--Community Review Round 1

rikki cattermole rikki at cattermole.co.nz
Thu Apr 4 18:58:58 UTC 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.


More information about the Digitalmars-d mailing list