new overloading model

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.

   * 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.

Received on Tuesday, 20 December 2011 02:15:40 UTC