On Tue, 24 Mar 2015 21:56:42 +0100, Boris Zbarsky <[email protected]> wrote:

Following Simon's move to public-fx; please respond in this thread, not the other one...

On 3/24/15 4:35 PM, Simon Pieters wrote:
[Constructor(optional DOMPointInit p1, optional DOMPointInit p2,
              optional DOMPointInit p3, optional DOMPointInit p4),
  Constructor(optional DOMRectInit rect),
....
* All arguments are optional. This is invalid WebIDL since it's not
possible to distinguish which constructor to use. For other constructors
I've let the dictionary be optional and have the other constructor have
two required arguments. We can do that here as well.

This is exciting. DOMRectInit and DOMPointInit are both dictionaries, right?

Yes.

WebIDL has this bit:

   If the type of an argument is a dictionary type or a union type that
   has a dictionary type as one of its flattened member types, and this
   argument is either the final argument or is followed only by optional
   arguments, then the argument MUST be specified as optional.

which means that at the moment the constructor version that takes 4 DOMPointInit arguments must in fact have them all be optional. So you can't do what you propose as things stand.

Yes, you're right.

While that requirement about having trailing dictionary arguments be optional makes sense for dictionaries used as options objects (where lack of the options object really had better be equivalent to passing {} in terms of API design), it doesn't really make much sense here, because these arguments aren't really options objects. In fact, if I saw someone doing this:

   new DOMQuad({}, {}, {}, {});

I would be pretty confused...

One plausible approach here would be to make the Web IDL spec change proposed in https://www.w3.org/Bugs/Public/show_bug.cgi?id=27953 and then make the members of DOMPointInit required and the DOMPointInit arguments non-optional.

I think we shouldn't require any members in particular here. It should be OK to do e.g.:

    new DOMQuad({}, {x:1}, {x:1, y:1}, {y:1});


But it's also worth running this whole idea by someone familiar with JS API design. The overload setup being proposed is basically relying on arguments.length to disambiguate which constructor was meant. How would JS code normally handle this situation? Possibly via arguments.length, but possibly via testing the first argument to see whether it has a .width property or whatever...

Yeah. We'll need to check the properties of the first argument to decide between rect or quad anyway. The question is if the argument length should be checked first to decide between points or (rect or quad). Since WebIDL overload checks argument length, it makes sense to me to be consistent here.

* If one does `new DOMQuad(other_domquad)`, it will convert the object
to a DOMRectInit and result in the same thing as `new DOMQuad({})` which
is probably not what was intended. Supporting two kinds of dictionaries
is not straightforward, though, since I think WebIDL doesn't distinguish
between different dictionaries.

Right, because they're all just objects and there's no really principled way to do it. You could distinguish in prose, of course, at the expense of having to decide _how_ to distinguish and writing the prose. Either via your proposal of having a dictionary that's the union of the two dictionaries, or by making the argument be "object" and doing the conversions to dictionary by hand.

Yeah I think that's probably a less messy approach.

Of course if you're doing that, you could just write it as:

   Constructor(optional DOMPointOrRectOrQuadInit arg1,
               optional DOMPointInit p2,
               optional DOMPointInit p3, optional DOMPointInit p4)

and then the prose would see which members of arg1 are present and if so decide whether to throw or look at p2-p4 or what.

I think it would be slightly surprising to interpret the first argument as rect or quad when there are 4 arguments.

How about this:

    [Constructor(optional object arg1, optional DOMPointInit p2,
                 optional DOMPointInit p3, optional DOMPointInit p4),
     Exposed=(Window,Worker)]
    interface DOMQuad { ... };

    dictionary DOMQuadInit {
      DOMPointInit p1;
      DOMPointInit p2;
      DOMPointInit p3;
      DOMPointInit p4;
    }

If invoked with zero arguments:
    Let /arg1/ be null _converted_ to a DOMQuadInit.
If invoked with one argument:
If /arg1/ _quacks_ like a DOMRectInit and like a DOMQuadInit, throw TypeError. If /arg1/ _quacks_ like a DOMRectInit, _convert_ /arg1/ to a DOMRectInit.
    Otherwise, _convert_ /arg1/ to a DOMQuadInit.
Otherwise:
    _Convert_ /arg1/ to a DOMPointInit.

A WebIDL value /object/ *quacks* like a dicationary type /type/ if _converting_ /object/ to /type/ results in a dictionary with one or more dictionary members present.

--
Simon Pieters
Opera Software

Reply via email to