Thank you for all the feedback. It has caused me to do a lot more thinking, which I’ll now try to reflect here.
@dan_t you’re right that the syntax for defining a local variable is very similar, so the alternative of real default values should stop complaining about the syntax for it. On the other hand, thinking about it more, teaching the limitations of what can be put in a default value should be a strong argument against that direction in my opinion.
Also, you make a good point about the API author not being able to require an explicit choice from the API user. Let’s state this in a drawback:
Drawback: the API author can not easily require the author to explicitly select a value for an Option-typed parameter.
Alternative: we could introduce a new symbol separating arguments in the function signature, to tell the compiler that only arguments after that symbol are really optional. This is informed by Python’s * argument separator that is used to introduce keyword-only arguments. Instead of the *, perhaps ? (surrounded by commas) can be used for this. I’m not sure this adds enough value to be worth the cost in function signatures with extra symbols that will require some teaching due to lack of familiarity.
Alternative: we could introduce a new, Option-like enum, like enum Default<T> { Value(T), Missing } which allows the API author to explicitly enable optionality of the argument. However, in naming this type, I would actually feel that Optional is a better name, which makes me question its value compared to Option.
@withoutboats interesting point about Into<Option<T>>. I guess that should result in an added alternative:
Alternative: allow the compiler replacing missing values with None for anything that implements Into<Option<T>>. My current feeling is that (alluding to your other point) doing so would make this proposal more magic, without a corresponding increase in ergonomics.
@Azerupi you’re right that putting this together with default values would result with semantics where there’s no “natural”, DWIM choice between two alternatives. From this, I came with up with more drawbacks and another alternative.
Drawback: this proposal makes a future default value proposal harder or impossible, since there is no natural resolution for a default value if the argument has an Option type. I would indeed argue that the remaining value of default values if this proposal is accepted is very limited, and so this proposal should be good enough so that default values will no longer be desired.
Drawback: for arguments that have a domain which includes a sentinel value where None is currently used (i.e. timeouts), this makes it impossible to have a default other than None. Here, I would argue that the alternative solution for this that comes to mind would be to introduce enum Timeout { Value(Duration), NoTimeout }, which I think is actually better in being more explicit about the meaning of the sentinel value (contrast None as not-a-value to None as sentinel-value). However, Option<Duration> is currently used in std for timeouts: 6 locations found in sys and net (though some of these are platform-dependent, and so they are probably not all independent). Note that this proposal, by itself, still does not create backwards incompatibility concerns (since existing code will keep working).
I guess I should make it more explicit that the underpinning of this proposal is that “not passing a value” feels pretty close semantically to “passing a None value”. However, perhaps I’ve understated this difference. Let’s add one more drawback and alternative.
Drawback: it might not be clear in the caller that some arguments are being added. While it is explicit, in the sense that you can clearly see how many arguments are being passed in, this might not be as obvious in functions with a larger number of arguments. It’s also not explicit-explicit, in the sense that there’s a marker to indicate that the compiler will extend the call with more arguments.
Alternative: add a marker token, like ..., to indicate in the caller that the compiler should fill in further arguments. However, this then makes the API evolution goal impossible, at least for the case of going from zero to one or more optional arguments.
Finally, I also wanted to state that I have a lot of experience with Python, which allows default values for arguments as well as named parameters (and more recently, keyword-only arguments). This proposal is informed by my experience that in Python, a large majority of all default values seem to be None. If useful/necessary, we could try to query all Python code in the GitHub data set to validate this assumption.