length property value for functions with parameter enhancements
On 27 August 2011 00:34, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Something we need to define for ES.next is how to compute the length property value for functions whose formal parameter list includes "optional" and/or rest parameters.
True, and actually, there are more issues with length & function proxies. I don't have my notes with me right now, but for example, it is not clear at all what length Function.prototype.bind should set when called on a function proxy. 0? 1? Should it try to get the length property from the proxy and subtract N? What if length is not defined on the proxy, or not a (natural) number?
This is probably something the proxy proposal has to resolve eventually, but it's worth keeping in mind for the broader picture.
So, what is a length determination algorithm that recognizes optional/rest arguments and is consistent with the stated intent of length and (as much as possible)existing section 15 definitions. Here is one proposal: The length is 0 only if the formal parameter list is empty. For example: function foo() {}; //foo.length==1
You meant ==0 here, right?
If the formal parameter list includes any non-optional, non-rest formal parameters, the length is the total number of non-optional/non-rest formal parameters. For example: function bar(a,b,c) {} //bar.length=3 function baz(a,b,c=0) {} //baz.length=2 function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal?
Makes sense. (And yes, I don't see why the latter shouldn't be legal.)
If there are no non-optional or non-rest formal parameters the length is 1. function bar1(a=0) {} //bar1.length=1 function baz1(a=0,b=1,c=2) {} //baz1.length=1 function bam1(...a) {} //bam1.length==1
I'm not so sure about this, it seems incoherent. Why not 0, especially for the first two? You mentioned builtins like Array above, but I would rather count them as the exception to the rule (especially given that the builtins don't seem entirely consistent wrt length anyway).
FWIW, one could also argue for setting length to +infinity for functions with only rest parameters. :)
On Aug 27, 2011, at 6:12 AM, Andreas Rossberg wrote:
On 27 August 2011 00:34, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Something we need to define for ES.next is how to compute the length property value for functions whose formal parameter list includes "optional" and/or rest parameters.
True, and actually, there are more issues with length & function proxies. I don't have my notes with me right now, but for example, it is not clear at all what length Function.prototype.bind should set when called on a function proxy. 0? 1? Should it try to get the length property from the proxy and subtract N? What if length is not defined on the proxy, or not a (natural) number?
The ES5.1 spec. defines how how bind determines the length for the function it creates based upon the length property of the target function. I would expect the same rules would apply when the target is a function proxy.
This is probably something the proxy proposal has to resolve eventually, but it's worth keeping in mind for the broader picture.
So, what is a length determination algorithm that recognizes optional/rest arguments and is consistent with the stated intent of length and (as much as possible)existing section 15 definitions. Here is one proposal: The length is 0 only if the formal parameter list is empty. For example: function foo() {}; //foo.length==1
You meant ==0 here, right?
right
If the formal parameter list includes any non-optional, non-rest formal parameters, the length is the total number of non-optional/non-rest formal parameters. For example: function bar(a,b,c) {} //bar.length=3 function baz(a,b,c=0) {} //baz.length=2 function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal?
Makes sense. (And yes, I don't see why the latter shouldn't be legal.)
Because the is a potential for misinterpreting the user intent on such a call. For bam('a','b',1,2,3) we surely have to interpret the argument/parameter mapping as: a='a',b='b',c=1,d=[2,3] but it is easy to imagine a programmer intending a='a',b='b',c=0,d=[1,2,3] Making it illegal to have a formal parameter list that has both optional and result parameters might result the likelihood of that confusion,
If there are no non-optional or non-rest formal parameters the length is 1. function bar1(a=0) {} //bar1.length=1 function baz1(a=0,b=1,c=2) {} //baz1.length=1 function bam1(...a) {} //bam1.length==1
I'm not so sure about this, it seems incoherent. Why not 0, especially for the first two? You mentioned builtins like Array above, but I would rather count them as the exception to the rule (especially given that the builtins don't seem entirely consistent wrt length anyway).
In my proposal, I am decided to make a clear distinction between truly empty formal parameter lists and those with only various forms of optional formal parameters by only giving a 0 length to the empty formals case. That's a debatable decision but it seems desirable to distinguish the two cases and the built-ins is the only precedent that we have to follow.
FWIW, one could also argue for setting length to +infinity for functions with only rest parameters. :)
But there is no precedent for that and surely infinity is not the "typical" number of arguments.
I'm not sure if there is any real use case for the length property of ECMAScript functions. Does anybody know of one? Regardless, I do think we can get rid of it.
On 29 August 2011 01:36, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On Aug 27, 2011, at 6:12 AM, Andreas Rossberg wrote:
True, and actually, there are more issues with length & function proxies. I don't have my notes with me right now, but for example, it is not clear at all what length Function.prototype.bind should set when called on a function proxy. 0? 1? Should it try to get the length property from the proxy and subtract N? What if length is not defined on the proxy, or not a (natural) number?
The ES5.1 spec. defines how how bind determines the length for the function it creates based upon the length property of the target function. I would expect the same rules would apply when the target is a function proxy.
Ah, right, I was looking at the 5.0 spec 8-}. However, that is still not good enough for function proxies, because you have no guarantee that they define length at all, or make it a natural number. So we at least have to include additional error cases, and a solution for them.
function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal?
Makes sense. (And yes, I don't see why the latter shouldn't be legal.) Because the is a potential for misinterpreting the user intent on such a call. For bam('a','b',1,2,3) we surely have to interpret the argument/parameter mapping as: a='a',b='b',c=1,d=[2,3] but it is easy to imagine a programmer intending a='a',b='b',c=0,d=[1,2,3] Making it illegal to have a formal parameter list that has both optional and result parameters might result the likelihood of that confusion,
Hm, that doesn't sound like a very JavaScripty argument :). If you buy into optional arguments at all, then I can certainly envision valid use cases for combining them with rest arguments.
If there are no non-optional or non-rest formal parameters the length is 1. function bar1(a=0) {} //bar1.length=1 function baz1(a=0,b=1,c=2) {} //baz1.length=1 function bam1(...a) {} //bam1.length==1
I'm not so sure about this, it seems incoherent. Why not 0, especially for the first two? You mentioned builtins like Array above, but I would rather count them as the exception to the rule (especially given that the builtins don't seem entirely consistent wrt length anyway).
In my proposal, I am decided to make a clear distinction between truly empty formal parameter lists and those with only various forms of optional formal parameters by only giving a 0 length to the empty formals case. That's a debatable decision but it seems desirable to distinguish the two cases and the built-ins is the only precedent that we have to follow.
I guess I don't see what is special about empty argument lists. Why would you want to make a clearer distinction between the argument lists () and (a=0), than between (x) and (x, a=0)? You seem to be introducing a discontinuity.
FWIW, one could also argue for setting length to +infinity for functions with only rest parameters. :)
But there is no precedent for that and surely infinity is not the "typical" number of arguments.
Yeah, I wasn't being serious.
I'm not sure if there is any real use case for the length property of ECMAScript functions. Does anybody know of one? Regardless, I do think we can get rid of it.
"Do not", I suppose? (Unfortunately, as I was wondering the same.)
On Aug 29, 2011, at 2:32 AM, Andreas Rossberg wrote:
On 29 August 2011 01:36, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
On Aug 27, 2011, at 6:12 AM, Andreas Rossberg wrote:
True, and actually, there are more issues with length & function proxies. I don't have my notes with me right now, but for example, it is not clear at all what length Function.prototype.bind should set when called on a function proxy. 0? 1? Should it try to get the length property from the proxy and subtract N? What if length is not defined on the proxy, or not a (natural) number?
The ES5.1 spec. defines how how bind determines the length for the function it creates based upon the length property of the target function. I would expect the same rules would apply when the target is a function proxy.
Ah, right, I was looking at the 5.0 spec 8-}. However, that is still not good enough for function proxies, because you have no guarantee that they define length at all, or make it a natural number. So we at least have to include additional error cases, and a solution for them.
those cases probably fall under step 16 of 15.3.4.5 but we'll have to be more explicit about that.
function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal?
Makes sense. (And yes, I don't see why the latter shouldn't be legal.) Because the is a potential for misinterpreting the user intent on such a call. For bam('a','b',1,2,3) we surely have to interpret the argument/parameter mapping as: a='a',b='b',c=1,d=[2,3] but it is easy to imagine a programmer intending a='a',b='b',c=0,d=[1,2,3] Making it illegal to have a formal parameter list that has both optional and result parameters might result the likelihood of that confusion,
Hm, that doesn't sound like a very JavaScripty argument :). If you buy into optional arguments at all, then I can certainly envision valid use cases for combining them with rest arguments.
Which is the source of the potential confusion because all of the "optional" parameters get populated with values before the rest argument gets any values.
...
In my proposal, I am decided to make a clear distinction between truly empty formal parameter lists and those with only various forms of optional formal parameters by only giving a 0 length to the empty formals case. That's a debatable decision but it seems desirable to distinguish the two cases and the built-ins is the only precedent that we have to follow.
I guess I don't see what is special about empty argument lists. Why would you want to make a clearer distinction between the argument lists () and (a=0), than between (x) and (x, a=0)? You seem to be introducing a discontinuity.
Given, that I have yet to think of a reasonable use case for function length properties, all of these rationalizations a pretty conceptual.
However, () seems like a clear statement that no arguments are required or expected while (a=0) says that an argument does have some meaning. This seems like a worthwhile distinction to make even though 1 may not be the "typical" number of arguments.
On Aug 28, 2011, at 4:36 PM, Allen Wirfs-Brock wrote:
On Aug 27, 2011, at 6:12 AM, Andreas Rossberg wrote:
On 27 August 2011 00:34, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
If the formal parameter list includes any non-optional, non-rest formal parameters, the length is the total number of non-optional/non-rest formal parameters. For example: function bar(a,b,c) {} //bar.length=3 function baz(a,b,c=0) {} //baz.length=2 function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal?
Makes sense. (And yes, I don't see why the latter shouldn't be legal.) Because the is a potential for misinterpreting the user intent on such a call. For bam('a','b',1,2,3) we surely have to interpret the argument/parameter mapping as: a='a',b='b',c=1,d=[2,3] but it is easy to imagine a programmer intending a='a',b='b',c=0,d=[1,2,3] Making it illegal to have a formal parameter list that has both optional and result parameters might result the likelihood of that confusion,
That's too restrictive. There are lots of ways to go wrong in JS, we don't try to put programmers in a five-point harness.
Python:
def foo(a,b,c=1,*d): ... print(a, b, c, d) ...
foo(0,1,2,3,4)
0 1 2 (3, 4)
foo(0,1,2,3)
0 1 2 (3,)
foo(0,1,2)
0 1 2 ()
foo(0,1)
0 1 1 ()
(Python reflects rest params [splats] as tuples not lists.)
Ruby is of course pretty similar:
def foo(a,b,c=1,*d) print a, b, c, d, "\n" end foo(0,1,2,3,4) foo(0,1,2,3) foo(0,1,2) foo(0,1) ^D 01234 0123 012 011
(My Ruby REPL skills are sad -- ugly run-together stdout follows the TTY EOF ^D above.)
If there are no non-optional or non-rest formal parameters the length is 1. function bar1(a=0) {} //bar1.length=1 function baz1(a=0,b=1,c=2) {} //baz1.length=1 function bam1(...a) {} //bam1.length==1
I'm not so sure about this, it seems incoherent. Why not 0, especially for the first two? You mentioned builtins like Array above, but I would rather count them as the exception to the rule (especially given that the builtins don't seem entirely consistent wrt length anyway).
In my proposal, I am decided to make a clear distinction between truly empty formal parameter lists and those with only various forms of optional formal parameters by only giving a 0 length to the empty formals case. That's a debatable decision but it seems desirable to distinguish the two cases and the built-ins is the only precedent that we have to follow.
You make a distinction but create an ambiguity between
function foo(a=0)... // length 1 function bar(a=0,b=1) // length 1
That seems no more consistent (and less coherent as Andreas put it). If trailing optional parameters do not contribute to length, then we have a consistent single rule for length. That it does not match all built-ins is sad history, not to be rewritten lightly but not to overdetermine the future.
I'm not sure if there is any real use case for the length property of ECMAScript functions. Does anybody know of one? Regardless, I do think we can get rid of it.
Working on instrumentation to find uses on the web. More in a bit,
On Aug 29, 2011, at 11:29 AM, Allen Wirfs-Brock wrote:
Hm, that doesn't sound like a very JavaScripty argument :). If you buy into optional arguments at all, then I can certainly envision valid use cases for combining them with rest arguments.
Which is the source of the potential confusion because all of the "optional" parameters get populated with values before the rest argument gets any values.
There's no confusion, IMHO. The order is left to right -- this matters for parameter default values in and of themselves. The precedent in nearby languages is strong. I am pretty opposed to making this an error case. It cannot of course be a case where right-to-left evaluation happens in any form (rest parameter "eats" actuals, leaving none in the middle for default parameters).
I guess I don't see what is special about empty argument lists. Why would you want to make a clearer distinction between the argument lists () and (a=0), than between (x) and (x, a=0)? You seem to be introducing a discontinuity.
Given, that I have yet to think of a reasonable use case for function length properties, all of these rationalizations a pretty conceptual.
However, () seems like a clear statement that no arguments are required or expected while (a=0) says that an argument does have some meaning. This seems like a worthwhile distinction to make even though 1 may not be the "typical" number of arguments.
You can't use one integer-valued property to convey two meanings. F.length is either minimum non-default/rest parameters, or a count of non-rest parameters. I don't see a sane alternative. The count of non-rest parameters is not that interesting, since it is one fewer than the full count of all parameters (modulo destructuring, where it must count each actual position, not all the names of destructured-to formals). That leaves F.length counting only non-default/rest params.
On Aug 29, 2011, at 1:36 PM, Brendan Eich wrote:
You can't use one integer-valued property to convey two meanings. F.length is either minimum non-default/rest parameters, or a count of non-rest parameters. I don't see a sane alternative. The count of non-rest parameters is not that interesting, since it is one fewer than the full count of all parameters (modulo destructuring, where it must count each actual position, not all the names of destructured-to formals). That leaves F.length counting only non-default/rest params.
I can think of two more plausible ones:
-
the number of parameter positions specified in the formal parameter list include (I'm avoiding talking about names because of destructing): function (a,b=1,...c) {} //length==3 function (...c) {} // length=1
-
the number of parameter positions (including the rest parameter if specified) that precede the first default-value parameter (or the end of the list if there are no default-value parameters): function (a,b,c) {} //length==3 function (a,b,...c) {} //length==3
function (...c) {} // length=1 function (a=1,b=2) {} //length==0 function (a,b=1) {} //length==1 function (a,b=1,...c) {} //length==1 -
is the simplest rule and in the absence of a clear use case is probably as good as any other
-
Treats rest as meaning "usually at least one" (unless following a default-value parameter) which seems to be the way open-ended parameter lists were interpreted in assigning length to most such Chapter 15 functions. I think that this rule would yield the most matches with the specified lengths of chapter 15 functions when their signatures are rewritten using default-value and rest parameter notation.
On Aug 29, 2011, at 4:38 PM, Allen Wirfs-Brock wrote:
- the number of parameter positions (including the rest parameter if specified) that precede the first default-value parameter (or the end of the list if there are no default-value parameters): function (a,b,c) {} //length==3 function (a,b,...c) {} //length==3
function (...c) {} // length=1 function (a=1,b=2) {} //length==0 function (a,b=1) {} //length==1 function (a,b=1,...c) {} //length==1 ...- Treats rest as meaning "usually at least one" (unless following a default-value parameter) which seems to be the way open-ended parameter lists were interpreted in assigning length to most such Chapter 15 functions. I think that this rule would yield the most matches with the specified lengths of chapter 15 functions when their signatures are rewritten using default-value and rest parameter notation.
I like this best, thanks for writing it out. The function(a,b=1,...){}.length === 1 condition in particular.
Ok, here are some results from a very brief browsing session with logging of F.length turned on for any function F:
images.nationalgeographic.com/wpf/sites/common/j/jquery/jquery_1.4.2.min-cb1282247955.js:91 4 images.nationalgeographic.com/wpf/sites/common/j/jquery/jquery_1.4.2.min-cb1282247955.js:91 0 pluck.nationalgeographic.com/ver1.0/Content/ua/scripts/pluckApps.js:266 4 test262.ecmascript.org/resources/scripts/global/jquery-1.4.2.min.js:91 4 test262.ecmascript.org/resources/scripts/global/jquery-1.4.2.min.js:91 0 static.ak.fbcdn.net/rsrc.php/v1/y1/r/RS_vQKTP5Zl.js:7 0 static.ak.fbcdn.net/rsrc.php/v1/y1/r/RS_vQKTP5Zl.js:7 0
The format is file:line length.
I need to fix SpiderMonkey to give column-based blame for those minimized jquery-1.4.2.min.js lines to be useful.
Anyway, plenty of evidence that F.length is used.
Here's a log with complete decompilation including the function whose .length was accessed.
file:line length function
It looks like that in a number of these cases, what is being tested is the number of declared arguments to the jquery Sizzle function which is a module function for its selector engine. Very early versions of Sizzle seem to have had 3 formals (jquery/sizzle/commit/51ba623126501dd9381040bbdcbabe12cc9056db#diff-0 ) but it fairly soon evolved to take 4 formals (jquery/sizzle/blob/8b061ee512c50910d4e8e0247084446d03e90aa3/sizzle.js ). I haven't found the code that does the actual test but this seems like the most likely reason to make such a test of that function.
Presumably that sort of version testings is s plausible use case for a function's length although it isn't clear that it is any better than just putting a version tag property on such a function.
Just want to clarify that neither jQuery nor its selector engine Sizzle have any occurrences of function arity checking via the function length property. Both codebases have occurrences of arguments.length checking
On Thu, Sep 1, 2011 at 11:38 AM, Rick Waldron <waldron.rick at gmail.com>wrote:
Just want to clarify that neither jQuery nor its selector engine Sizzle have any occurrences of function arity checking via the function length property. Both codebases have occurrences of arguments.length checking
FTR this kind of arity checking is pretty common in the node.js world: kriszyp/promised-io/blob/master/promise.js#L619
On Sep 1, 2011, at 8:55 AM, Dean Landolt wrote:
On Thu, Sep 1, 2011 at 11:38 AM, Rick Waldron <waldron.rick at gmail.com> wrote: Just want to clarify that neither jQuery nor its selector engine Sizzle have any occurrences of function arity checking via the function length property. Both codebases have occurrences of arguments.length checking
FTR this kind of arity checking is pretty common in the node.js world: kriszyp/promised-io/blob/master/promise.js#L619
To clarify, it is indeed function arity checking that node.js is performing. In this case, it is assuming (maybe "requiring" is closer to fact) that all "async functions" take a callback as their last argument. and using that fact to generate wrapper functions that replace the callback function.
This pattern would break under the proposal we are discussing if somebody tried to define an "async function" with default value or rest parameters. However, it would also break today if somebody tried to define an "async function" that solely used its arguments object to access arguments and neglected to declare any formal parameters. Presumably in either case node.js should tell its users not to use such parameter patterns in defining "async functions".
Regardless of whether or not we counted optional value parameters in determining the value of a function's length property this node.js pattern would be problematic if callers actually made use of the ability to optionally include/exclude default value arguments. So, it's not clear that this use case helps us much in design our length value algorithm.
Something we need to define for ES.next is how to compute the length property value for functions whose formal parameter list includes "optional" and/or rest parameters. (See harmony:parameter_default_values and harmony:rest_parameters ). Remember that according to 15.3.5.1the value of the length property of a function is "the 'typical' number of arguments expected by the function" and that for user defined functions, the value of the length property as currently specified in 13.2 is the number of items in the functions formal parameter list.
For built-in, section 15 says that the length property value for built-ins "unless otherwise specified, this value is equal to the largest number of named arguments shown in the subclause headings for the function description, including optional parameters". However, there are many cases where the length is "otherwise specified" and in many of those the pattern seems to be to not include optional parameters in the length.
Some examples,
Built-in constructors most commonly have a length of 1. This includes Array which can be viewed as using a rest parameter. However, Date has a length of 7 reflecting that it can have up to 7 optional parameters and RegExp has a length of 2 even though its second argument is arguably optional (although it isn't shown as such in the section heading). But overall, these generally the follow the section 15 default rule of counting optional parameters.
Array.prototype and String.prototype functions generally don't include optional parameters in the length count and they generally give a function that only has a single rest parameter (for example, concat and push) a length of 1 but don't include any rest parameters in the length for functions such as splice where the rest parameter is not the firs parameter.
Date.prototype has a number of functions that take optional arguments and they are all counted in the length.
(BTW, it would be a useful exercise for somebody to try to recode all the built-in function section headings using optional/rest parameter syntax)
So, what is a length determination algorithm that recognizes optional/rest arguments and is consistent with the stated intent of length and (as much as possible)existing section 15 definitions. Here is one proposal:
The length is 0 only if the formal parameter list is empty. For example: function foo() {}; //foo.length==1 If the formal parameter list includes any non-optional, non-rest formal parameters, the length is the total number of non-optional/non-rest formal parameters. For example: function bar(a,b,c) {} //bar.length=3
function baz(a,b,c=0) {} //baz.length=2 function bam(a,b,c=0,...d) {} //bam.length==2 BTW, is this legal? If there are no non-optional or non-rest formal parameters the length is 1. function bar1(a=0) {} //bar1.length=1 function baz1(a=0,b=1,c=2) {} //baz1.length=1 function bam1(...a) {} //bam1.length==1
Thoughts?