Open badeend opened 2 years ago
I've also wondered about having default values too, so this is a great issue to raise.
One reason for the current design with option
is that option
conveys an extra bit of semantic information from the caller to the callee (distinguishing the cases of a caller not supplying a value vs. explicitly supplying what happens to be the default value). In most situations, this doesn't matter, but it seems like in some scenarios it's useful:
option
gives the per-language bindings more flexibility for picking the language-idiomatic to deal with missing values. There's a lot of variability here when you dig into the fine details (e.g., witness the subtle distinctions between null
vs. undefined
vs. option
, the interface says "however your language expresses a missing parameter, do that, then communicate this to me as the none
case.option
, it's quite clear that the behavior of the none
case is controlled by the callee.So all that made me lean toward the current option
design; but maybe there are other reasons to prefer default values?
One obvious one is that, when the none
case implies a default value (which is not always, but perhaps most of the time), it's more explicit to include that default value in the programmatic interface, rather than relying on documentation. At some point, I think we'll want to standardize a "documentation" custom section with structured contents (rather than free-form strings) and, in that context, a structured "default value" annotation could make sense and be programmatically available to, e.g., intellisense.
When posed as an either/or question, I'm ambivalent. I don't think default values are inherently better than option types or vice versa. They can both fill each other's role to some extent.
That said, I think the two concepts are orthogonal. Especially in languages that support both. For instance, these three are not the same:
void sendMail(bool asHtml = true);
void sendMail(Option<bool> asHtml);
void sendMail(Option<bool> asHtml = None);
Having only option types at their disposal forces programmers to introduce an extra state in the system where this might not be appropriate. In the preceding example, asHtml
ended up with a third limbo state "None", besides true and false.
Another example is the flags
interface type. Either it must be special cased in the subtyping rules or you can't add new flags in a backwards compatible way.
Yeah, I suppose I see your point; sometimes you don't want to add a new None
case, but you also don't want folks to have to fill in every flag/field/parameter manually.
With option, the interface says "however your language expresses a missing parameter, do that, then communicate this to me as the none case.
I think languages like Java, C#, etc. would most likely use Option
for their implicit nullability of all reference types. Leaving them no other way to communicate missing parameters. (Other than a nested Option<Option<<...>>
?)
Default values raise some subtle questions/hazards with respect to version upgrade (...)
Could this be solved by requiring that, in case both the subtype and supertype declare a default value for a particular field/parameter, the values must be compatible (according to the subtyping rules) ? That way it wouldn't matter whether the caller or the callee is responsible for default values; the end result is effectively the same.
Though, with that restriction in place, I think it makes sense to make the caller responsible for providing the default values. See examples below.
One reason for the current design with option is that option conveys an extra bit of semantic information from the caller to the callee (distinguishing the cases of a caller not supplying a value vs. explicitly supplying what happens to be the default value)
If that distinction is truly needed, you can still use an Option type. No harm done.
At some point, I think we'll want to standardize a "documentation" custom section with structured contents (rather than free-form strings) and, in that context, a structured "default value" annotation could make sense and be programmatically available to, e.g., intellisense.
I don't think the documentation/annotation/metadata direction is enough whenever the subtype contains a field/parameter with a default value the supertype is not aware of. See example 2 below. In this case the component model glue code has to be aware of default values.
(...) it seems overall simpler to not have a constant-value expression language; the subtyping algorithm seems like it would be roughly similar.
I completely understand. I'll leave that tradeoff for you to make.
I think languages like Java, C#, etc. would most likely use Option for their implicit nullability of all reference types. Leaving them no other way to communicate missing parameters. (Other than a nested Option<Option<<...>> ?)
My expectation here is that the language bindings start with the interface type (which does not have implicit nullability, only option<T>
) and then coerces to and from that. So if the bindings accept a source-language reference for a non-nullable interface-type T
, then on source-language null
, the bindings would either trap
, throw
or coerce in some language-bindings-defined manner. Thus, option<T>
would not be forced to inherit the default-nullability of languages.
Could this be solved by requiring that, in case both the subtype and supertype declare a default value for a particular field/parameter, the values must be compatible (according to the subtyping rules) ?
That's an interesting idea! Yes, I suppose theoretically it could. It seems like this would put further requirements on this constant-valued interface-typed expression language, though.
Thanks for writing out all those examples! I hadn't even thought about the interaction between default values and function subtyping allowing extra optional parameters... some of those cases were rather surprising to me and it seems like we'll need to think long and hard about this.
Nice to see this fleshed out proposal. Having to deal with None v Some branches in all places where things are optional does create overhead that is nicer to avoid with default values.
Is the intention for the "default" default implementations of types to be defined? Can we extend the proposal semantics to the WIT definitions at this point?
TLDR, these proposed changes:
option
type from its special status in various places.Definition
Regarding the
list
constant: every provided<constant>
must be a subtype of the list's<intertype>
.Aliases
Types
Every constant expression has a fully deterministic interface type.
Updates to existing definitions
This is already more detailed than I intended it to be. Let me know if there is interest in pursuing this any further.