Re: Reconciling handling of optional arguments and handling of default values across ES and webidl

> Boris Zbarsky <mailto:bzbarsky@MIT.EDU>
> September 27, 2013 8:58 AM
>
>
> OK, but then it seems like the reflection on length should be useful 
> in the "it should actually reflect how many arguments you need to call 
> the function with" sense.

Yes. As Cameron cited from ECMA-262:

"The value of the length property is an integer that indicates the 
typical number of arguments expected by the function."

Of course then we must define "typical".

As you say, WebIDL is more expressive than JS, about variadicity and 
optional parameters. We should take full advantage.

/be
>
> -Boris
> Brendan Eich <mailto:brendan@mozilla.com>
> September 27, 2013 8:55 AM
>
>
> People do care, when making function wrappers that must mock up 
> .length correctly to inform some client that reflects on .length for 
> whatever reason. This came up on es-discuss recently:
>
> August thread: 
> https://mail.mozilla.org/pipermail/es-discuss/2013-August/033004.html
> July thread: 
> https://mail.mozilla.org/pipermail/es-discuss/2013-July/031971.html
>
> /be
>
>
> Boris Zbarsky <mailto:bzbarsky@MIT.EDU>
> September 26, 2013 10:40 PM
> On 9/27/13 1:25 AM, Domenic Denicola wrote:
>> This does not match ES6, where it would be zero if you wrote
>>
>> function foo(arg1 = undefined, arg2) {
>
> That's true.  But why would you write that?
>
> Allen and I spent a while talking about this last week, with not much 
> agreement....
>
> The current ES6 .length rules are basically designed around the fact 
> that ES6 doesn't have a good concept of "how many arguments does this 
> function expect?" so it has to guesstimate that based on the parameter 
> list.
>
> But in WebIDL we are actually giving more information about how many 
> arguments the function expects.
>
> So given this WebIDL:
>
>   void foo(optional long arg1, long arg2);
>
> I would claim that the right ES expression for it (that would match 
> the actual WebIDL semantics) would be:
>
>   function foo(arg1, arg2) {
>     if (arguments.length < 2) throw new TypeError("Need 2 args");
>     if (arg1 !== undefined) {
>       arg1 = arg1 >> 0;
>     }
>     arg2 = arg2 >> 0;
>   }
>
> or so.
>
> A bigger problem is this WebIDL:
>
>   void foo(optional long arg1 = 5, long arg2);
>
> How to map that to ES is interesting: you could use a default value on 
> the formal parameter, or you could just check in the function body.... 
> and the .length would be different depending on how you did it.
>
> You could make a case that we should shoehorn this stuff into the 
> limitations of ES formal parameter lists somehow, but I believe the 
> spirit of .length, that it be the number of arguments the function 
> typically expects, is better served by the definition Cameron has in 
> the spec right now.
>
>> I suppose you could not map "optional" to "= undefined"
>
> Indeed.  It's not = undefined at all.
>
>> but that seems most natural
>
> Why?
>
>> and fits well with how I assume default arguments in WebIDL should be 
>> mapped to ES.
>
> Maybe.
>
> In some ways this whole discussion about .length is pointless, since 
> in practice no one really cares about what .length is on functions, I 
> suspect....  But if we're going to have this feature (.length on 
> functions that's supposed to mean something) I think we should in fact 
> have it mean something.
>
> -Boris
> Domenic Denicola <mailto:domenic@domenicdenicola.com>
> September 26, 2013 10:25 PM
>> On Sep 26, 2013, at 5:17, "Cameron McCormack"<cam@mcc.id.au>  wrote:
>>
>> Boris Zbarsky wrote:
>>> 1) In the new setup, I believe this is valid IDL:
>>>
>>> void foo(optional long arg1, long arg2);
>> Yes.
>>
>>> Is this purposeful?
>> Yes, so that you can pass undefined for arg1 and have the prose do something based on that.
>>
>>> It looks at first glance like this function should have length 2,
>>> since in
>>> http://dev.w3.org/2006/webapi/WebIDL/#dfn-effective-overload-set step
>>> 5.7 we'll start with i == 1 and discover that "argument i of X" (does
>>> it mean "X's argument at index i"?) is not optional, so the shortest
>>> element of the overload set will have two arguments, right?
>> Yes.  Since arg2 must be specified, I think it makes more sense to have length be 2 rather than 0.  WDYT?
>
> This does not match ES6, where it would be zero if you wrote
>
> function foo(arg1 = undefined, arg2) {
>    if (arg2 === undefined) throw new TypeError("arg2 required");
> }
>
> I suppose you could not map "optional" to "= undefined", but that seems most natural, and fits well with how I assume default arguments in WebIDL should be mapped to ES.
>
>>> 2) I'm not convinced about the changes to the variadic handling. In
>>> particular, consider this function:
>>>
>>> void foo(long... argList);
>>>
>>> and an invocation of it like so:
>>>
>>> foo(5, undefined, 6);
>>>
>>> when the effective overload set is computed for this call, we get the
>>> following tuples:
>>>
>>> <foo, (long), (true)>
>>> <foo, (), ()>
>>> <foo, (long, long), (true, true)>
>>> <foo, (long, long, long), (true, true, true)>
>>>
>>> then we remove all but the last entry from the list. Now we start doing
>>> the argument conversions and invoke the callee with the values 5,
>>> |special value "missing"|, 6.
>>>
>>> That seems pretty odd to me. Are there use cases for having missing
>>> variadic args? If not, I'd prefer we keep the old behavior, where all
>>> the variadics (except perhaps trailing undefined, which would simply not
>>> be passed on to the callee?) are coerced to the right type. Otherwise we
>>> have to fix all specs using variadics to deal with the "missing" case...
>> Yes, I think that's fair enough.  If we feel we do want to support "explicit undefined =>  missing optional argument" in variadic positions later we can make "(optional long... argList)" or something mean that.
>>
>> It's also kind of weird to treat trailing undefined differently from middle-of-the-variadic-arguments undefined, though, so I'm inclined to have them all get coerced to the argument type.
>>
>> I'm going need to store the variadic-ness, not just the optional-ness, in the tuples for that, I think.  Change coming soon...
>>
>>> 3) I don't understand step 10.2 of the overload resolution algorithm.
>>> Why is this needed, exactly?
>> If you have
>>
>>   void f(long x, long y);
>>   void f(long x, optional Node n);
>>
>> and you call
>>
>>   f(0, undefined);
>>
>> then S is
>>
>>   {<f_1, (long, long), (false, false)>,
>>     <f_2, (long, Node), (false, true)>  }
>>
>> just after step 3, and the distinguishing argument index, d, is 1.
>>
>> Step 10.2 looks at the undefined value that was passed in, matches it against the optional Node argument of f_2, and so selects that overload.
>>
>>> Nits:
>>>
>>> 4) There is a typo in "followed only be optional arguments" when talking
>>> about dictionary types.
>> Fixed.
>>
>>> 5) In the above discussion of overload sets for functions with 1
>>> variadic argument, I assumed that when n == 1, t_{0...n-2} means "empty
>>> list". It might be worth being more explicit about that somehow... Note
>>> that this empty list would be added by effective overload set step 5.8
>>> anyway, so there is no harm in restricting step 5.5.1 to the case when n
>>>> 1.
>> Yes, the t_{0..n-2} is meant to be like t.slice(0, n - 1) if it were a JS array.  I agree the notation I'm using there for lists isn't great. Hopefully it is clear that it doesn't mean remove two items from the end, given the usage in step 5.3 etc., where it's declaring a whole list.  But I've added a note in 5.1 to say that it means it leaves off the variadic argument.
>>
>>
>
> Boris Zbarsky <mailto:bzbarsky@MIT.EDU>
> September 10, 2013 9:18 AM
>
>
> Sorry it took me so long to get to this.
>
> Substantive comments:
>
> 1)  In the new setup, I believe this is valid IDL:
>
>   void foo(optional long arg1, long arg2);
>
> Is this purposeful?  It looks at first glance like this function 
> should have length 2, since in 
> http://dev.w3.org/2006/webapi/WebIDL/#dfn-effective-overload-set step 
> 5.7 we'll start with i == 1 and discover that "argument i of X" (does 
> it mean "X's argument at index i"?) is not optional, so the shortest 
> element of the overload set will have two arguments, right?
>
> 2) I'm not convinced about the changes to the variadic handling.  In 
> particular, consider this function:
>
>   void foo(long... argList);
>
> and an invocation of it like so:
>
>   foo(5, undefined, 6);
>
> when the effective overload set is computed for this call, we get the 
> following tuples:
>
> <foo, (long), (true)>
> <foo, (), ()>
> <foo, (long, long), (true, true)>
> <foo, (long, long, long), (true, true, true)>
>
> then we remove all but the last entry from the list.  Now we start 
> doing the argument conversions and invoke the callee with the values 
> 5, |special value "missing"|, 6.
>
> That seems pretty odd to me.  Are there use cases for having missing 
> variadic args?  If not, I'd prefer we keep the old behavior, where all 
> the variadics (except perhaps trailing undefined, which would simply 
> not be passed on to the callee?) are coerced to the right type.   
> Otherwise we have to fix all specs using variadics to deal with the 
> "missing" case...
>
> 3)  I don't understand step 10.2 of the overload resolution algorithm. 
> Why is this needed, exactly?
>
> Nits:
>
> 4) There is a typo in "followed only be optional arguments" when 
> talking about dictionary types.
>
> 5) In the above discussion of overload sets for functions with 1 
> variadic argument, I assumed that when n == 1, t_{0...n-2} means 
> "empty list".  It might be worth being more explicit about that 
> somehow... Note that this empty list would be added by effective 
> overload set step 5.8 anyway, so there is no harm in restricting step 
> 5.5.1 to the case when n > 1.
>
> -Boris

Received on Friday, 27 September 2013 16:06:05 UTC