Re: new overloading model

On Tue, 20 Dec 2011 03:15:06 +0100, Cameron McCormack <cam@mcc.id.au>  
wrote:

> There are several currently open issues that relate to operation  
> invocation:
>
>    * a request for union types [LC-66]
>      http://www.w3.org/Bugs/Public/show_bug.cgi?id=14188
>
>    * a (now withdrawn, but probably not unreasonable) request to be able
>      to distinguish objects and arrays when overloading [LC-67]
>      http://www.w3.org/mid/op.v1y3anre64w2qv@annevk-macbookpro.local
>
>    * a request to remove [AllowAny] and make it the default for
>      DOMString when overloading [LC-77]
>      http://www.w3.org/mid/op.v3oo09cz64w2qv@annevk-macbookpro.local
>
>    * a need to make not-exactly-matching types passed to overloaded
>      operations behave the same as non-overloaded operations
>      http://www.w3.org/mid/4EDD94BA.4000503@mcc.id.au
>
> I think we can satisfy the above requirements regarding overloading by  
> further restricting the situations where it's allowed and by relying on  
> union types.  The main restriction that we make is to disallow  
> overloading between primitive types (float, bool, long, etc.) and  
> DOMString.  There are no APIs at the moment that need this.
>
> So the following is disallowed:
>
>    void f(Node n, float x);
>    void f(Node n, DOMString x);
>
> As is this:
>
>    void g(Node n, float x, DOMString y);
>    void g(Node n, DOMString x, float y);
>
> which is a case where simply preferring DOMString conversions over  
> others would not solve the overload resolution when called like
>
>    g(node, "ab", "cd");
>
> unless we look at the ordering of the operations in the IDL.  Simon  
> showed where that's not going to work.
>
> The following is still allowed, since overloading discriminates based on  
> argument list length before looking at argument types:
>
>    void h(Node n, float x, float y);
>    void h(Node n, DOMString x);
>
> Union types would be allowed to specify a primitive type and DOMString  
> in the one union.  For conversion from JS values in this case, DOMString  
> would be preferred.  That allows the first invalid example above to be  
> rewritten as:
>
>    void f(Node n, DOMString or float x);
>
> while the second invalid example could be rewritten as:
>
>    void g(Node n, DOMString or float x, DOMString or float y);
>
> and then, if necessary, prose can be used to throw an exception if x and  
> y are both strings or both floats.
>
> The new definition of distinguishable will exclude (primitive,  
> DOMString), but union types will allow any combination of  
> distinguishable types in addition to allowing a primitive type and  
> DOMString to coexist.
>
> The rule remains that for each pair of overloaded operations, there must  
> be at least one argument index where the two types at that position are  
> distinguishable.  This means that the following is allowed:
>
>    void i(Node n, float x);
>    void i(float x, Node n);
>
> but that when called as
>
>    i(1, 2);
>
> the overload resolution algorithm would end up with no candidates, and  
> an exception would be thrown.
>
> Two union types are distinguishable if all elements of the first are  
> distinguishable with all elements of the second.
>
> Note that primitive types and DOMString are the only types that can get  
> values from inexactly matching JS values passed in -- a Boolean is not  
> needed for bool (ToBoolean is used to convert), a String is not needed  
> for DOMString (ToString is used), etc.  All the other types -- interface  
> types, object, dictionaries, sequences, arrays, Date -- do not get any  
> conversions; either the JS value passed in is an exact match for the  
> type or it isn't.
>
> Per http://www.w3.org/mid/4EEFE632.9010506@mcc.id.au I think we should  
> remove the ability for plain, non-Array JS objects to be considered as  
> sequences.  Objects like NodeLists can still be considered sequences,  
> but rather than assuming this by looking at its length property, we just  
> look at the indexed properties on the object.  Interfaces with indexed  
> properties are now not distinguishable from sequence types.
>
> We should also disallow overloading between two interface types if it is  
> possible for an object to implement both interfaces.  To handle such  
> cases, spec authors can just use a union type instead.
>
>
> Ignoring the issue of explicitly passing undefined meaning "consider  
> this an un-passed optional argument" and whether we want to revisit that  
> (http://www.w3.org/mid/4EEEA0F0.5010906@mcc.id.au), overload resolution  
> in this proposal goes as follows:
>
>    * Generate the "effective overload set" for the given identifier
>      and argument list length.  Union types do not get expanded out
>      into multiple entries like optional arguments do.
>
>    * For each argument position:
>
>        * For each entry in the effective overload set:
>
>            * If the type is one of the exactly matching types mentioned
>              above, or is a union type consisting only of the exactly
>              matching types, and the JS value passed in does not match
>              it, then disqualify this entry.

For nullable types, null is considered to match?

What about

void f(DOMString? x);
void f(Node? x);

Both would qualify if null is passed in (actually even if the DOMString  
type isn't nullable).

Similarly, what about nullable unions?

void f(DOMString? or Node? x);

>    * Assert: either all or all but one entries are disqualified.
>
>    * If there is one entry left not disqualified, success, that's the one
>      we call.  Otherwise, throw.
>
>
> With all of the above:
>
>    * We no longer need [AllowAny], as DOMString is always the selected
>      type when overloading and an inexactly matching JS value is passed
>      in.
>
>    * We can have sequences where the element type does not have to be
>      widened to "any" just so we can handle, say, Nodes and DOMStrings.
>      We can just write sequence<Node or DOMString> instead.
>
>    * We simplify a bunch of overloading that is done just to support
>      multiple interface types in one argument position.  It means we can
>      simplify drawImage from CanvasRenderingContext2D:
>
>        typedef HTMLImageElement or HTMLCanvasElement or HTMLVideoElement
>                DrawableElement;
>
>        void drawImage(DrawableElement image,
>                       double dx, double dy);
>        void drawImage(DrawableElement
>                       double dx, double dy, double dw, double dh);
>        void drawImage(DrawableElement image,
>                       double sx, double sy, double sw, double sh,
>                       double dx, double dy, double dw, double dh);
>
>    * We don't need to pay attention to the order of overloads given in
>      the IDL.
>
>    * We can write operations that overload distinguishing dictionaries
>      and sequences.
>
>    * We don't have the current undesirable behavior where an inexact
>      match when calling an overloaded operation causes an exception to
>      be thrown.
>
> In the HTMLOptionsCollection example Simon brought up in the other  
> thread, where the current IDL is:
>
>    void add(HTMLOptionElement element, optional HTMLElement? before);
>    void add(HTMLOptGroupElement element, optional HTMLElement? before);
>    void add(HTMLOptionElement element, long before);
>    void add(HTMLOptGroupElement element, long before);
>
> If we call add(document.createElement("optgroup"), "1") then we now  
> would correctly choose the fourth overload, since any inexact type  
> matches select long rather than HTMLElement?.
>
> Those four overloads could be simplified into one using union types, if  
> desired:
>
>    void add(HTMLOptionElement or HTMLOptGroupElement element,
>             optional HTMLElement? or long before);
>
>
> Again, thoughts on all of the above are welcome, especially if you find  
> any problems with it.

Overall looks good to me!

-- 
Simon Pieters
Opera Software

Received on Tuesday, 20 December 2011 10:13:31 UTC