Re: [geometry] Dictionary argument for DOMQuad constructor

On Tue, 24 Mar 2015 21:56:42 +0100, Boris Zbarsky <bzbarsky@mit.edu> 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

Received on Wednesday, 25 March 2015 08:36:42 UTC