-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sort out when dictionaries and records should have default values #76
Comments
Would it be possible to say something like "when an ECMAScript value is not supplied for the argument, the corresponding Web IDL value is the empty dictionary"? I don't quite understand why you need a default value.
I guess this doesn't seem like a big deal to me; having a canonical form with the same keys every time seems like a plus, even if their values are not terribly useful. But I don't feel strongly, so we can change it if you want.
Indeed, this seems like a good thing in fact.
Overall sounds sane, although if there are cases where {} and undefined are treated differently, then I guess we are in trouble. It sounds like the Bugzilla comment 6 concern was aleviated in that regard though? Will we never have such a case? |
Sure. We already say something like that that for dictionaries in trailing position, as I noted.
The only cases where that happens are:
|
This needs to be sorted out for records too... |
Automatically eliding empty dictionaries might make it harder to provide invariants to JS, like: if (pc.getParameters().rtcp.cname) // TypeError: pc.getParameters(...).rtcp is undefined That might be correct in this particular case, but as a general rule, I could see it causing edges if a returned dictionary is non-empty most of the time (but not always). |
The workaround of marking |
@domenic @tobie @annevk This is causing problems because specs are coming up with APIs that involve optional dictionaries that have required members: something that never works as an argument but technically works as a dictionary member right now, breaking the symmetry between "list of arguments" and "a dictionary with some members". So we need to decide whether we want to allow breaking this symmetry and if not we need to change IDL accordingly so people stop trying to do that. |
I think I'd argue that a dictionary member that is a dictionary should have a default value as well.
Invoking I don't think there's a good reason to make dictionary handling inconsistent between it being an argument and a member of another dictionary. (According to bz on IRC Firefox already does this.) |
@annevk Semantically, doesn't that seem odd, since if someone wanted that they can already use:
? There's also the issue of at least two specs doing this already, and probably not meaning what I wrote. See https://bugzilla.mozilla.org/show_bug.cgi?id=1368949 |
@jan-ivar similarly to how we require dictionaries to always be optional arguments, we should probably also forbid the combination with |
But we don't. We can write: dictionary Foo { required long foo1; };
dictionary Bar { required Foo myFoo; };
void doFoo(Foo myFoo);
void doStuff(Bar myBar);
Function arguments are required by default whereas dictionary members are optional by default, so I don't see what consistency we'd be upholding. At least two specs interpret: dictionary Foo { required long foo1; };
dictionary Bar { Foo myFoo; }; to mean
says: "If configuration.video is present and is not a valid video configuration, return a Promise rejected with a TypeError. " I.e. they expect |
Specifically for dictionaries with a required member, because those correspond to a set of arguments at least one of which is required.
Dictionaries as initially designed are meant to be equivalent to a bunch of trailing optional arguments, but easier to use because you can specify only the arguments you want instead of all the ones before the last one you want (with undefined for the ones you don't want). If JS supported named-argument passing like Python, this pattern likely would never have been invented in the first place; it's there to work around the fact that JS only has positional arguments. |
And arguably adding required members to dictionaries was a weird decision: now people have to decide when to make an argument an actual (non-optional) argument and when to make it a required dictionary member... |
@bzbarsky > Dictionaries as initially designed are meant to be equivalent to a bunch of trailing optional arguments Does that still needs to hold though? Like @jan-ivar I find it hard to justify that consistency especially when it's not consistent now. Why would one inconsistency preferred over another? |
Does what need to hold? Dictionaries are conceptually a bag of arguments. Adding required doesn't really change this, apart from allowing required arguments in the bag. Some specs are sort of trying to have nested bags of arguments (or more precisely to represent actual data structures of some sort, not just bags of arguments). That's where the issues come in... |
That "Dictionaries as initially designed are meant to be equivalent to a bunch of trailing optional arguments" What the child or is define does should have no bearing on its parent definition. like any OOP. |
The problem is that you want to distinguish between |
(In response to bz) I think the weird decision would be to say all required arguments must be positional. I think we left out the advent of the nested dictionary. Arguably, that's when we invited complexity. At least two specs have shown they want rules about when arguments are required based on (grouping of) other arguments. The question is whether WebIDL can express it. |
(In response to annevk) |
Also, to the consistency argument: Regardless of history, the semantics chosen are not POLA and seem internally inconsistent. E.g.: dictionary Foo { required long foo1; };
dictionary Bar { Foo myFoo = null; };
void doStuff(optional Bar myBar); Jib.doStuff(); // TypeError: Missing required 'foo1' member of Foo. Note how we don't get “ WebIDL.WebIDLError: error: Dictionary argument without any required fields or union argument containing such dictionary not followed by a required argument must be optional, /Users/Jan/moz/mozilla-central/dom/webidl/Jib.webidl line 18:19 So it literally says the argument to |
Sure, because of the details of exactly how the overload algorithm works. The argument is flagged as optional, technically, but if you don't pass it it's defaulting to a value that's not actually valid based on the other IDL there... I'm not quite sure what your argument is, honestly.
Um... what's ignored? The point is that |
I think it's worth taking a step back for a second. WebIDL consumers can always define arbitrary processing of things as desired; Actual WebIDL syntax is meant to cover common patterns and promote best practices. This is why dictionary arguments treat Now the question is what should be the best practice for this "nested dictionary" thing. Are there examples of such APIs in JS libraries, designed by actual JS developers? Because all the examples in specs pointed to above were created by C++ developers, and historically those are not very well clued-in to how JS APIs should look and act. |
Sounds like a true scotsman argument. I pointed out that the syntax itself is counter intuitive. That seems true regardless of who wields the tool. Having things labeled optional which are effectively required, seem like semantic footguns, even for the most experienced. It's also not to spec, so where would someone read and learn this logic, in order to program, as you say, as an "actual JS developer"? |
@emlun under your proposal, |
Yes, I suppose so - unless |
You can't do
|
…onal dictionary. r=bz Summary: In order to allow for optional dictionaries with required members See whatwg/webidl#76 for more information. Reviewers: bzbarsky Tags: #secure-revision Bug #: 1471165 Differential Revision: https://phabricator.services.mozilla.com/D1833
Ah, thanks, my mistake.
Is this the way it currently is, or what you're suggesting in OP? My suggestion in #76 (comment) is that |
This is the way it currently is. To be clear, in the current spec for function arguments, For other dictionaries, still in the current spec, This is why I wanted to double-check what your actual suggestion is: it sounds like you want to treat |
Ok. Yeah, my inexperience with WebIDL is probably obvious at this point. I suppose the exact semantics of explicit |
Explicit |
If explicit null means empty (truly empty, no members) dictionary, then null is an illegal value for any argument whose type is a dictionary with required members. I'm OK with that. It probably is worth calling out explicitly. |
Yes, |
OK. So if we stick to just arguments... Currently the spec only defaults trailing optional dictionary arguments to empty dictionary. Is there a reason not to do this for non-trailing ones? |
You mean in the event that someone passes undefined? Would that currently pass undefined through to the algorithm? |
Yes. So given this IDL:
and this JS:
is there a reason to support the "foo" arg being missing as opposed to being an empty dictionary (or more precisely a dictionary with an |
If we can I'd prefer forbidding such APIs, but otherwise defaulting makes sense I suppose. |
We can certainly forbid it. I'm not sure when the ability to add non-trailing optional arguments was added. https://www.w3.org/Bugs/Public/show_bug.cgi?id=10336 was asking for it but was wontfixed. None of the other issues or bugzilla tickets seem obviously relevant, and digging through blame with all the mass-reformattings is hard... I can do it if really needed, though; I suspect it's blame on the overload resolution algorithm and its handling of |
Perhaps as a stopgap we could allow setting dictionary members as nullable if they're required dictionaries? e.g. dictionary Foo {required bool thing;};
dictionary Bar {Foo? foo;} stumbling across this while designing XRTest, which isn't a web-exposed API but it doesn't seem like an uncommon pattern |
@Manishearth What is the exact usecase? Note that you can already write:
and this will do the right thing if you initialize |
Hmm. I might be confused as to the current state of things. |
…onal dictionary. r=bz Summary: In order to allow for optional dictionaries with required members See whatwg/webidl#76 for more information. Reviewers: bzbarsky Tags: #secure-revision Bug #: 1471165 Differential Revision: https://phabricator.services.mozilla.com/D1833 UltraBlame original commit: 3f1d5e7ef2449764526ec086f27c1550516dcdc6
…onal dictionary. r=bz Summary: In order to allow for optional dictionaries with required members See whatwg/webidl#76 for more information. Reviewers: bzbarsky Tags: #secure-revision Bug #: 1471165 Differential Revision: https://phabricator.services.mozilla.com/D1833 UltraBlame original commit: 3f1d5e7ef2449764526ec086f27c1550516dcdc6
…onal dictionary. r=bz Summary: In order to allow for optional dictionaries with required members See whatwg/webidl#76 for more information. Reviewers: bzbarsky Tags: #secure-revision Bug #: 1471165 Differential Revision: https://phabricator.services.mozilla.com/D1833 UltraBlame original commit: 3f1d5e7ef2449764526ec086f27c1550516dcdc6
Right now optional dictionary arguments in trailing position have a default value (empty dictionary) but other optional dictionaries (non-trailing arguments, dictionary members) do not.
This allows spec authors to create APIs in which undefined and empty dictionary have different behavior, which seems suboptimal.
So I'd like to propose that optional dictionaries always have empty dictionary as default value. What that means in practice is not clear. I guess they should use
null
as the default value. Sadly that's observably different from using{}
if someone spews things onObject.prototype
...Anyway, if we do that, then we have one problem: dictionary-to-JS conversion can give ugly results (as described in https://bugzilla.mozilla.org/show_bug.cgi?id=1226475#c0 for example).
To solve that, I propose the following:
For dictionaries that have default values that will still cause them to appear (with those default values), but I can live with that, I think.
@domenic/@heycam Thoughts?
The text was updated successfully, but these errors were encountered: