- From: Cameron McCormack <cam@mcc.id.au>
- Date: Tue, 20 Dec 2011 13:15:06 +1100
- To: public-script-coord@w3.org
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