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