Wow, nice summary! Thank you for your efforts.
For my part, I feel like trying to jump to a solution that uses structs rather than simply using Option
for default arguments seems like it introduces unnecessary complexity. Since the proof-of-concept will rely on Macros, why not simply have the macro create a function that wraps all arguments with default parameters in Option
and inserts the code inside the function definition to map the Option
to the default value if it is None
. Then on the calling side, just sub in Option<None>
for all not provided parameters with the macro that does the calling side. This seems much more straightforward then using an intermediate struct.
EDIT: It seems like a good way to handle getting to Named Parameters in a step-wise fashion would be:
- Make the compiler able to elide Optional Parameters on Functions/Methods
- Add annotation to opt-out of Optional Parameter elision at the definition site - Also, implement annotation or syntax for API to define a default value beyond
Option<T>/None
and T:Default
- Make naming parameters at call site optional (either all named, or none named) in order
- Add annotation to opt-out of either named or non-named parameters at the definition site
- Allow eliding name of parameter in parameter calling convention if the variable being passed exactly matches the name of the elided parameter name (and the name is not already given on another parameter)
- Add an Opt-In at the definition site for allowing out-of-order named calling convention
- Add ability to overload function/method names that require named calling convention with multiple versions with different sets of named parameters
So, how would this look? First, an RFC and PR to allow #1. This would make any parameter that is Option<T>
or T : Default
permitted to be left off from the call site. This would be a backwards compatible, non-breaking change because it would not break in any way existing call sites or existing API’s (that weren’t already broken). Calls would be required to still include parameters in order of definition, but, Option<T>
and T : Default
parameters could be left out - the compiler would automagically insert None
or T::Default()
as appropriate for elided parameters at the call site.
Next, an RFC and PR for #2. This would allow the definition site to mark Option<T>
and T : Default
parameters as Non-Optional. This would be a breaking change to an API (only if the API chose to exercise this option), but, if done soon after #1, would not have a wide-ranging impact and could be done only on a case-by-case basis where the API deems it necessary. It seems like this is unlikely to happen very often as it would only enforce something for the purposes of explicitness not for correctness. Also, we could allow the API author to annotate specific parameters as having specific default values for any parameter in the function/method definition and have these parameters treated as default parameters as well. This should be 100% backwards compatible and require no API breakage.
Then, an RFC/PR could then enable #3 the ability to call any method/function with named or unnamed calling convention. No mixing of the two would be permitted. It is either call using all parameters (except elided optional parameters) with no names in definition order or calling all parameters (except elided optionals) with named arguments (still in definition order). This again, would be a 100% backwards compatible, non-breaking change.
Now, an RFC for #4, the ability for the definition site to opt-out of either Named or Unnamed calling convention. If no annotation is given, then, the method/function can be called using either calling convention. If the annotation is given, that either opts for forcing either named or unnamed convention, then, that selected convention would be required at call sites. This again could be a breaking change for a given API (only if the API chose to exercise this option), but, again, it seems unlikely that this option would need to be exercised often as it again would be about explicitness and enforcing a particular idiomatic calling style not correctness.
Now, an RFC/PR could be implemented for #5 to allow eliding the name of a parameter in the named parameter calling convention style if the name of the variable being passed exactly matches the name of the parameter and that parameter is not otherwise already named in the call. This would be a 100% backwards compatible change and would not break any existing API’s or call sites.
After that, add an RFC/PR for #6, the ability of the definition to opt-in to allowing out-of-order named calling convention. Without explicit opt-in, then in-order is enforced at the call site. With the opt-in the call site may call using named-parameters in an out-of-order fashion. This again is a 100% non-breaking backwards compatible change.
Finally, add and RFC/PR for #7, the ability to have multiple overloaded, named calling convention methods/functions, of the same name. This would allow method overloading based on the set of named parameters and would only be permitted for methods/functions that required named calling convention. In other words, the names of the parameters become part of the method/function name implicitly. This may or may not be a breaking change for API’s to add overloaded methods/functions depending on how this is specifically implemented. For example, if could be non-breaking if the first definition of a named-parameter only calling convention method did not make the implicit name of the method function depend on the names of the parameters but following definitions did. I really something like this could be useful/desirable and implemented in an API backwards compatible fashion.
I think a plan like the above breaks off the work into manageable chunks with no significant breaking changes anywhere in the process. It also leaves room for bike-shedding on specific syntax issues and permits opt-in/op-out where that seems prudent without introducing breaking-changes at the point where that functionality becomes available. In addition, each item stands on its own and we could stop anywhere in the process and still have the usefulness of the previous items without breaking anything or precluding future movement on the other items.
Thoughts?
BTW: I hope this isn’t taken as me attempting to hijack your discussion or ideas. I really like what you’ve summarized and am excited to see movement on this. I 100% would like to have named parameter calling convention and I’d be more interested in getting there than how we get there specifically, but, I felt that the above thoughts could provide some positive contribution to the discussion.