Re: Notes from Monday's meeting with TC39 folks

On Wed, Oct 7, 2009 at 10:50 PM, Maciej Stachowiak <mjs@apple.com> wrote:
>
> On Oct 7, 2009, at 9:57 PM, Cameron McCormack wrote:
>
>> On Monday, Brendan, Allen, Mark, Jonas had a second ad hoc meeting to
>> discuss Web IDL.
>
> Are these meetings getting announced in advance anywhere?

I tend to find out about them about 15 minutes after they start :)

>>  Below are some rambling notes.
>>
>>
>> I didn’t agree with the part of Allen’s analysis of overloading that
>> claimed that it currently requires expensive overload resolution
>> whenever a function is called (the implementation should know ahead of
>> time exactly what set of ES types correspond to which overloaded
>> operation).
>
> Yes, the current approach should not be any more expensive than an approach
> with union types. Either way you do some type checks and dispatch to the
> proper piece of code.
>
>> There was agreement though about the “impedence mismatch”
>> when considering ES as the most important language binding, and using
>> union types seemed like a plausible way of doing this.  Specs currently
>> tend to consider them in prose as the same operation/function anyway.
>>
>> Moving to union types would help discourage certain poor designs, such
>> as canvas’ createImageData(), where certain arguments change their
>> meaning depending on how many are passed.  If we restrict ourselves to
>> union types plus the current way of specifying optional arguments, then
>> it does make some patterns more difficult to specify.  In particular, if
>> with the current overload mechanism we had
>>
>>  void f(in DOMString s);
>>  void f(in DOMString s, in float x, in float y);
>>  void f(in Element e);
>>  void f(in Element e, in float x, in float y);
>>
>> then we’d need need to write this as
>>
>>  void f(in DOMString | Element a,
>>        in optional float x,
>>        in optional float y);
>>
>> thus allowing two arguments to be passed, if you just look at the IDL,
>> and then prose would be required to disallow the two argument call.  We
>> could go back to the way [Optional] used to be specified, which meant
>> that that argument and all following could be omitted, but I found that
>> to be a bit unclear.  (Currently, overloading is required in the spec to
>> handle this case.)
>>
>> Another thing I just noticed is that it doesn’t allow you to specialise
>> the return type depending on which overload is invoked.  I can’t
>> remember seeing any web API that currently has different return types on
>> overloaded operations.
>
> How would you specify canvas's getImageData() with this approach? Or
> drawImage()? Seems like it is impossible to specify the interface correctly
> with the union type mechanism. It's one thing to "discourage", but if IDL
> can't specify the behavior of existing interfaces, then it will fail in its
> purpose.

I definitely agree that union types + [optional] provides less
expressiveness than overloading. For example you can't say that a
function can only take 0 or 2 floats. Or can only take one two floats
or an ImageData (like CanvasRenderingContext2D.createImageData).

However you can always describe that in prose. We'll never have a IDL
language that can express all the needed constraints. For example the
ability to say that a float argument must be non-negative, or non-NaN,
or say that if a boolean argument is missing it should default to
'true'. It'll always be a judgement call what to allow IDL to express,
and what needs to be put in prose. For example the IDL for
CanvasRenderingContext2D.drawImage might actually be easier to
understand if it was expressed in prose. It currently has 6 overloads,
half of which has [optional] arguments.

One possible place to draw the line is how much you'd want a
scripting<->C++ conversion layer to support. For example, would you
expect such a layer to enforce all of the requirements for drawImage?
In gecko we currently only have one drawImage function in our internal
IDL which the conversion layer always dispatches to, that function
then does all the checking.

It's possible that other conversion layers would do things differently
(in fact, i'd be surprised if that wasn't the case). And there's
definitely other ways to draw the line for what goes into the IDL.

>> With this union types model, Null would become a built-in type in the
>> IDL.  As in the current spec, the string type (DOMString; maybe we can
>> reconsider renaming it again) would not have null as one of its values.
>> A union type would be needed to allow null as a distinct value, e.g.:
>>
>>  attribute (DOMString | Null) x;
>>
>> and the ‘?’ suffix currently used to denote a nullable type could be
>> syntactic sugar for that:
>>
>>  attribute DOMString? x;
>>
>> (And of course typedefs could be used to make union types more usable.)
>>
>> We talked about having an Undefined type in the IDL, too, but I’m not
>> sure whether we came to an agreement on that.  If we did have it, then
>> we should distinguish the case where the Undefined type is accepted as
>> an argument value with the case where the argument is omitted.
>>
>> The thought is that there are few APIs where it makes sense to
>> distinguish null and "" when accepting a DOMString (getAttribute being a
>> notable one).  But we will need to check.  There are two possibilities
>> on how ES values are coerced to DOMString by default:
>>
>>  A) null is converted to ""
>>    anything else is converted using the String constructor
>>
>>  B) everything is converted using the String constructor
>>
>> If an analysis shows that the former covers most cases (majority?
>> plurality?) then we would go with that and also not allow overriding
>> this in the IDL as you can do with [Null] (or [TreatNullAs], as it
>> became).
>
> I think the default should be (A) but it should be possible to override for
> specific APIs, if needed for legacy behavior. This is my biggest problem
> with nullability as it exists in the current spec. The common case
> ("DOMString?") is longer and more funny-looking than the rare case
> ("DOMString").
>
>>
>> With regards to how the undefined value should be coerced to a string,
>> Jonas said (paraphrasing):
>>
>>  A lot of people have argued that we should always use the String
>>  constructor so undefined gets converted to "undefined" (and similarly
>>  for null).  I see logic in that too.  What makes the least amount of
>>  sense is what we do in Gecko, where null comes "" and undefined
>>  becomes "undefined".  I think we should recommend converting both to
>>  the empty string and seeing what people say.  We can use union types
>>  to handled undefined differently if necessary.  I think IE generally
>>  stringifies both null and undefined.
>
> Many of the DOM Core and DOM Events interfaces specifically mandate treating
> null the same as the empty string, and in at least some cases this works
> interoperably.
>
> I think changing the behavior for undefined could be a compatibility risk.

I'd be very interested to gather data here. I suspect that *in
general* null and undefined are rarely passed to these functions, so I
think we're a little free to play around with behavior here. There are
exceptions of course, for example I suspect treating null as "null" in
the namespaceURI argument for setAttributeNS would break the web.

While Gecko by default treats null as the empty string, I was under
the impression that most (all?) other major UAs by default treat null
as "null". And that for example WebKit used IDL extended attributes
for when to treat null as "". (This is one of the reasons I suspect
people don't generally depend on behavior here.)

If so, where do you end up having to explicitly treat null as ""?

As for undefined, I think UAs fairly consistently treat that as "undefined".

If it turns out that WebKit only in a few places end to treating null
as "", then I think we should make the default to use the String
constructor for all DOMString arguments, and in the few cases where
null needs to be treated as "", annotate that in the IDL. If that
annotation is done as a "DOMString | null", "DOMString?", or
"[Null=''] DOMString" I care less about.

If it turns out we have to annotate the exception in a lot of places,
my second preferred option would be to treat both null and undefined
as "", unless annotated differently.

>> And Mark said:
>>
>>  We shouldn’t be considering converting null to "" but undefined to
>>  "undefined".
>
> But that's what most browsers do for most existing DOM APIs (with quirky
> exceptions). So we must consider it.

I agree we should consider it.

>> Later discussion, assuming that there was no Undefined type in the IDL,
>> suggested that the Null IDL type could be specified as either null or
>> undefined in ES, but when returned from the implementation to ES would
>> always be denoted using null.
>
> Since null and undefined are often treated differently, this seems like a
> bad idea.

Really? And how do you mean "treated differently". For strings, see
discussion above, too many unknowns at this point for me to give a
firm answer. For objects, it seems like at least gecko treats
undefined as null, need to check other UAs.

There's also things like pushState, which takes an "any" argument
which is then cloned using the "structured clone" algorithm. Here I
agree that null and undefined needs to be treated differently in the
sense that null should be cloned to null and undefined to undefined.

>> We also discussed removing the null value from interface types, which
>> would require IDL writers then to use union types to explicitly allow
>> null.  For example:
>>
>>  attribute Node? firstChild;   /* or (Node | Null) */
>>  void appendChild(in Node n);
>>
>> where firstChild can be null, but the value passed to appendChild
>> cannot.  We’d define a TypeError (or something) to be thrown if null was
>> passed to appendChild here.
>
> Yuck. Many DOM APIs can return null, having to annotate all of them seems
> like a lot of work for low return.

I think we currently have a problem where lots of functions take
objects, but do not define behavior when null is passed in. I'd like
to fix this by making it explicit in the IDL when null is an
acceptable in-value, and in WebIDL define what exception is thrown if
null is passed in where not allowed. Just like WebIDL should describe
what exception to pass to a function that takes a Node but is passed a
string.


>> I had some issues with [Supplemental] effectively allowing an interface
>> to lose members depending on which set of specs you consider when
>> generating a Java interface, which would cause binary compatibility
>> problems.  (You want to only allow methods to be added to an interface.)
>> In Java you handle these cases with mixin interfaces, as is currently
>> done with various DOM specs (e.g. DocumentCSS, DocumentView).  I think
>> it might be better to allow these to have unique interface names, like
>> DocumentCSS, but with an extended attribute that indicates explicitly
>> what prototype object is being supplemented with new properties — maybe:
>>
>>  [Supplemental]
>>  Document implements DocumentCSS;
>>
>> or so.
>
> Does retroactively adding a mixin preserve Java binary compatibility?

Adding properties to IDL does not preserve binary compatibility in the
general case no. For example if a function was added to EventListener
then no existing code would implement that function, meaning things
would fail when the function is called.

However this problem already exists. DOM Level 3 adds lots of new
functions to the Node interface. In Gecko we ended up having to add a
Node3 interface in order to maintain binary compatibility. The same
thing could be done for mixin methods added by another spec.

Basically I don't think it's possible to at the same time:
* Maintain binary compatibility between releases
* Add support for new specifications in new releases (be that newer
version of already supported specifications or entirely new
specifications)
* Maintain a 1-to-1 mapping between the IDL in the specs and the IDL
in the product.

One of the three have to be sacrificed. I'd expect most people
sacrifice the third bullet, that's what we've done in Gecko so far.

>> Those of us who were able to stay until the end of the meeting (all but
>> Allen) were happy with a few initial recommendations, but I
>> unfortunately don’t have a note of them.  I believe they included:
>>
>>  * Not allowing callable objects in future APIs
>
> If it's allowed, it's allowed - at best we can say it SHOULD NOT be used in
> new APIs. I don't think it would be appropriate to grandfather in specific
> interfaces from other specs by name, and a requirement that doesn't name the
> specific interfaces would have no teeth.

Gecko only supports callable on the document.all collection. I'm fine
with attempting to remove that and see how much people complain. I've
never heard anyone ask for callability on other collections.

Microsoft is apparently looking into if they can remove callable.

I'd say lets not put callable into the specifications. UAs are always
free to implement additional features that aren't in the spec,
everyone already does. Once we've tried to remove callable and it
turns out to break the web we can always add it to the spec then.

/ Jonas

Received on Thursday, 8 October 2009 19:26:03 UTC