repeated parameter names and default values

# Allen Wirfs-Brock (13 years ago)

Consider

function f(a, b=a, a=3, c=a) { return a+" "+b+" "+c }

console.log(f(1));

Based upon the conclusions about argument binding at the recent TC39 meeting this will display: "3 1 3"

Any disagreement of this?

# Andrea Giammarchi (13 years ago)

it would be stupid to code like that but it makes sense since it has basically always been like that :)

function f(a) { // var a superflous ... still works var a, b = a, a = 3, c = a; return a+" "+b+" "+c; }

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 9:31 AM, Andrea Giammarchi wrote:

it would be stupid to code like that but it makes sense since it has basically always been like that :)

A big part of of my job is specifying what stupid code does.

# Andrea Giammarchi (13 years ago)

I did not mean to offend anyone ... it just looked weirdest, non-sense, function contract, ever :D

Apologies and thanks for describing even these details in specifications, appreciated!

# David Bruant (13 years ago)

...this is how I discovered that, in non-strict mode, it's possible to define a function where 2 arguments have the same name...

Le 27/09/2012 18:19, Allen Wirfs-Brock a écrit :

Consider

function f(a, b=a, a=3, c=a) {

The most positive adjective I can find to describe this like is: "confusing". If I'm ever in a team where a dev writes such a line, I will kill this dev with my bare hands :-) When writing 'c=a', what was the intention of the developer? Maybe the third arg has been renamed to 'a' by mistake during a refactoring, introducing a subtle bug.

When using the same argument name several times with default values, what about throwing an SyntaxError at parse time regardless of strictness? It would follow what's been done with strict mode.

Also, is there really a value in being able reuse a previous argument to define the value of another argument? The idea of "for the second argument, use its value unless it's undefined in which case, reuse the value of the first argument" smells like poorly designed code or API, but I'm curious if some can show a case where it's legitimate.

# David Bruant (13 years ago)

Le 27/09/2012 18:41, Allen Wirfs-Brock a écrit :

On Sep 27, 2012, at 9:31 AM, Andrea Giammarchi wrote:

it would be stupid to code like that but it makes sense since it has basically always been like that :) A big part of of my job is specifying what stupid code does.

I'm begging, please throw early errors as much as you can! There is no reason for the language to encourage stupid or confusing code. Default arg values are a convenience introduced to improve readability. It's been introduced after the experience of functions starting with several "x = x || v" lines. I wish it was kept to that and not introduce new ways to write confusing code.

# Oliver Hunt (13 years ago)

On Sep 27, 2012, at 9:45 AM, David Bruant <bruant.d at gmail.com> wrote:

...this is how I discovered that, in non-strict mode, it's possible to define a function where 2 arguments have the same name...

Le 27/09/2012 18:19, Allen Wirfs-Brock a écrit :

Consider

function f(a, b=a, a=3, c=a) { The most positive adjective I can find to describe this like is: "confusing". If I'm ever in a team where a dev writes such a line, I will kill this dev with my bare hands :-) When writing 'c=a', what was the intention of the developer? Maybe the third arg has been renamed to 'a' by mistake during a refactoring, introducing a subtle bug.

When using the same argument name several times with default values, what about throwing an SyntaxError at parse time regardless of strictness? It would follow what's been done with strict mode.

I actually like this, it simplifies the semantics if you say using default arguments disallows duplicate names. This I think is a livable restriciton as allowing duplicate arguments is basically just a backwards compat feature (so obviously doesn't effect code using defaults).

I kind of like the idea of disallowing shadowing of argument names if defaults are used as well, but I'm not sure if that would be as low risk to comprehension of behaviour.

# Jason Orendorff (13 years ago)

On Thu, Sep 27, 2012 at 11:19 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

function f(a, b=a, a=3, c=a) { return a+" "+b+" "+c } console.log(f(1));

Based upon the conclusions about argument binding at the recent TC39 meeting this will display: "3 1 3"

Not having been at the meeting, I had a little trouble coming up with a desugaring where this worked. Is this the idea?

function f(a, b, a, c) {
    var %args = a copy of arguments;
    a = %args[0];
    b = %args[1] === void 0 ? a : %args[1];
    a = %args[2] === void 0 ? 3 : %args[2];
    c = %args[3] === void 0 ? a : %args[3];
    return a+" "+b+" "+c;
}

(Of course I understand it won't be specified in terms of desugaring but through changes to 10.5.3, "Function Declaration Instantiation").

For what it’s worth, I’m with David and Oliver: by all means, just make that a SyntaxError.

# Mark S. Miller (13 years ago)

On Thu, Sep 27, 2012 at 9:41 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Sep 27, 2012, at 9:31 AM, Andrea Giammarchi wrote:

it would be stupid to code like that but it makes sense since it has basically always been like that :)

A big part of of my job is specifying what stupid code does.

Although Allen does so mostly for non-security reasons, I'll take this moment to make a security point:

"Normal" non-defensive programming generally seeks to avoid edge cases, and especially edge cases where platforms are likely to differ.

Attackers see such edge cases as opportunities. Whereas a correct program should work on all conforming platforms, an attack is successful even if it only succeeds on one supported platform.

Therefore, defensive programs, though they should still stay away from edge conditions for the functionality they provide, must worry about and defend themselves against all the possible adversary behaviors that these edge conditions might enable.

Thus, a language that supports defensive programming needs this kind of careful attention to "stupid" edge conditions.

This is not to argue for or against any specifics of this proposal.

# Allen Wirfs-Brock (13 years ago)

Another:

The notes from last week's meeting said WRT function parameter bindings:

Conclusion/Resolution

  • var bindings and are in scope within the function
  • cannot use let to shadow a parameter
  • defaults can refer to any top level binding

Using the above guidance, consider this one:

function test1(b=g) [ function g() {}; return typeof g +" "+ typeof b; } console.log(test1());

presumably, this produces: "function function" Which means that g is instantiated and initialized prior to apply the defaults to the parameters.

Now consider:

function test2(g,b=g) [ function g() {}; return typeof g +" "+ typeof b; } console.log(test2());

By the same logic, this presumably must also produce: "function function"

So how about:

console.log(test2(1));

Is it: "function number" or "function function" or "number number"

In ES5,

function test3(g) [ function g() {}; return typeof g; } console.log(test3(1));

produces "function", so the "number number" alternative doesn't seem plausible. In EX5, nested function declarations over-write parameters that share the same name.

"function number" may seem reasonable for the second call of test2 but that seems inconsistent with the first call and with test1.

So, it seems that nested function declarations must be instantiated and bound before parameter instantiation/default value initialization and that parameters that share a name with a such function declaration do not get reinitialized (with either an argument or default value) during parameter instantiation.

# Andrea Giammarchi (13 years ago)

as described here

function f(a) { // var a superflous ... still works var a, b = a, a = 3, c = a; return a+" "+b+" "+c; }

same / equivalent result

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 10:03 AM, Oliver Hunt wrote:

On Sep 27, 2012, at 9:45 AM, David Bruant <bruant.d at gmail.com> wrote:

...this is how I discovered that, in non-strict mode, it's possible to define a function where 2 arguments have the same name...

Le 27/09/2012 18:19, Allen Wirfs-Brock a écrit :

Consider

function f(a, b=a, a=3, c=a) { The most positive adjective I can find to describe this like is: "confusing". If I'm ever in a team where a dev writes such a line, I will kill this dev with my bare hands :-) When writing 'c=a', what was the intention of the developer? Maybe the third arg has been renamed to 'a' by mistake during a refactoring, introducing a subtle bug.

When using the same argument name several times with default values, what about throwing an SyntaxError at parse time regardless of strictness? It would follow what's been done with strict mode.

I actually like this, it simplifies the semantics if you say using default arguments disallows duplicate names. This I think is a livable restriciton as allowing duplicate arguments is basically just a backwards compat feature (so obviously doesn't effect code using defaults).

I kind of like the idea of disallowing shadowing of argument names if defaults are used as well, but I'm not sure if that would be as low risk to comprehension of behavior.

My original intent was to apply such rules and the current spec. draft actually has some of these restrictions. However, those parts were written before 1JS emerged and the consensus around 1JS seems to be try to gracefully extend the current ES5 semantics to encompass such new feature without early error restrictions.

Also, at the last meeting there was some significant push back about the load-time costs (startup latency) of early error detection.

# Andrea Giammarchi (13 years ago)

quoting myself:

Apologies and thanks for describing even these details in specifications, appreciated!

as summary: I know this is a must have too :-)

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

So, it seems that nested function declarations must be instantiated and bound before parameter instantiation/default value initialization and that parameters that share a name with a such function declaration do not get reinitialized (with either an argument or default value) during parameter instantiation.

Yes, this was what I remember us agreeing to the other week (was it last week? A blur...).

It's the "simpler desugaring wins" case. Parameters are var-like and defaulting happens in the body.

Separately, I agree with David, Oliver, Jason, Mark and probably everyone that new syntax can mean new checks pulled in from strict mode. Duplicate formal parameters with defaulting? Error!

We did this in SpiderMonkey for duplicate formals + destructuring, and the same argument applies there. I don't remember what our default parameter impl does with dups, though.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

My original intent was to apply such rules and the current spec. draft actually has some of these restrictions. However, those parts were written before 1JS emerged and the consensus around 1JS seems to be try to gracefully extend the current ES5 semantics to encompass such new feature without early error restrictions.

We have discussed here doing what SpiderMonkey already does: the strict-mode ban on duplicate parameter names when you opt into destructuring. This is entirely consistent with 1JS, and it makes the language better for users and implementors, from what I've seen.

Also, at the last meeting there was some significant push back about the load-time costs (startup latency) of early error detection.

That's an issue for sure but we talked last week about less-than-early errors for some things. OTOH even a stripped down parser or "reader" must cope with some name binding checks per ES5 strict mode (as well as recognizing "use strict";). That ship sailed. Same goes for duplicate property names in object literals in strict mode.

The real objection was to full name def/use analysis, which this is not. Duplicate formals are pretty easy to detect and you have to throw an early error for any such given a "use strict"; (later!) in the same function.

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 9:45 AM, David Bruant wrote:

... Also, is there really a value in being able reuse a previous argument to define the value of another argument? The idea of "for the second argument, use its value unless it's undefined in which case, reuse the value of the first argument" smells like poorly designed code or API, but I'm curious if some can show a case where it's legitimate.

David

function doSomethingToAnArray(a, limit=a.length) { ... }

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 10:09 AM, Jason Orendorff wrote:

On Thu, Sep 27, 2012 at 11:19 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

function f(a, b=a, a=3, c=a) { return a+" "+b+" "+c } console.log(f(1));

Based upon the conclusions about argument binding at the recent TC39 meeting this will display: "3 1 3"

Not having been at the meeting, I had a little trouble coming up with a desugaring where this worked. Is this the idea?

function f(a, b, a, c) { var %args = a copy of arguments; a = %args[0]; b = %args[1] === void 0 ? a : %args[1]; a = %args[2] === void 0 ? 3 : %args[2]; c = %args[3] === void 0 ? a : %args[3]; return a+" "+b+" "+c; }

Yes, essentially that plus presumably appropriate length checks on %args.

Also, I assume by "arguments" you mean the actual passed argument vector rather than the arguments object.

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 10:26 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

My original intent was to apply such rules and the current spec. draft actually has some of these restrictions. However, those parts were written before 1JS emerged and the consensus around 1JS seems to be try to gracefully extend the current ES5 semantics to encompass such new feature without early error restrictions.

We have discussed here doing what SpiderMonkey already does: the strict-mode ban on duplicate parameter names when you opt into destructuring. This is entirely consistent with 1JS, and it makes the language better for users and implementors, from what I've seen.

Also, at the last meeting there was some significant push back about the load-time costs (startup latency) of early error detection.

That's an issue for sure but we talked last week about less-than-early errors for some things. OTOH even a stripped down parser or "reader" must cope with some name binding checks per ES5 strict mode (as well as recognizing "use strict";). That ship sailed. Same goes for duplicate property names in object literals in strict mode.

The real objection was to full name def/use analysis, which this is not. Duplicate formals are pretty easy to detect and you have to throw an early error for any such given a "use strict"; (later!) in the same function.

So the purpose my original posting is achieved. I'm all for statically disallowing many of these "stupid" cases and I think it is fine to do the checks at "first call" rather than load-time (a off-line static linter could be more aggressive in reporting such errors). Since the consensus here seems to be the same, I'll take that approach as I update the spec.

# Douglas Crockford (13 years ago)

On 2012-09-27 9:19 AM, Allen Wirfs-Brock wrote:

Consider

function f(a, b= a=3, c=a) {
   return a+" "+b+" "+c
}

console.log(f(1));

Based upon the conclusions about argument binding at the recent TC39 meeting this will display: "3 1 3"

Any disagreement of this?

A constantly recurring question in this effort is "Do we want it to be consistent, or do we want it to be better?"

I am always in favor of better. So parameter names should not be repeatable.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

On Sep 27, 2012, at 10:26 AM, Brendan Eich wrote:

Also, at the last meeting there was some significant push back about the load-time costs (startup latency) of early error detection. That's an issue for sure but we talked last week about less-than-early errors for some things. OTOH even a stripped down parser or "reader" must cope with some name binding checks per ES5 strict mode (as well as recognizing "use strict";). That ship sailed. Same goes for duplicate property names in object literals in strict mode.

The real objection was to full name def/use analysis, which this is not. Duplicate formals are pretty easy to detect and you have to throw an early error for any such given a "use strict"; (later!) in the same function.

So the purpose my original posting is achieved.

Ok, but...

I'm all for statically disallowing many of these "stupid" cases and I think it is fine to do the checks at "first call" rather than load-time (a off-line static linter could be more aggressive in reporting such errors).

No. As a matter using accurate language, that's not "statically".

More, you didn't respond to my "use strict"; point. Are you going to change whether duplicate parameters with "use strict"; in the function prologue throw an early error, or not?

Since the consensus here seems to be the same, I'll take that approach as I update the spec.

We have not discussed error-on-first-call in this thread at all!

# Bill Frantz (13 years ago)

On 9/27/12 at 9:41, allen at wirfs-brock.com (Allen Wirfs-Brock) wrote:

A big part of of my job is specifying what stupid code does.

This job is a vital job if you want a completely specified language. (BTW - I know of no completely specified languages, or other computer system components for that matter, but the closer the specification comes to being complete, the better). Congratulations to Allen on the job he is doing for Javascript.

Cheers - Bill


Bill Frantz | Truth and love must prevail | Periwinkle (408)356-8506 | over lies and hate. | 16345 Englewood Ave www.pwpconsult.com | - Vaclav Havel | Los Gatos, CA 95032

# Andreas Rossberg (13 years ago)

On 28 September 2012 03:30, Bill Frantz <frantz at pwpconsult.com> wrote:

(BTW - I know of no completely specified languages, or other computer system components for that matter, but the closer the specification comes to being complete, the better).

Off-topic, but I can't resist pointing out here that (Standard) ML is a fairly sophisticated general purpose language that not only has had a complete, formal specification for 2 decades [1], it even has a fully machine-verified proof of correctness and type safety [2]. I also know of at least 3 research projects working on a mechanised formalisation of JavaScript. That is to say that thoroughly specifying and formalising languages is no longer rocket science (while mechanising them admittedly is).

That said, the ES spec, while ugly as an aye-aye, is relatively thorough (and Allen is working on making it better).

/Andreas

[1] www.itu.dk/people/tofte/publ/1990sml/1990sml.html [2] repository.cmu.edu/cgi/viewcontent.cgi?article=1908&context=compsci

# John Lenz (13 years ago)

On Thu, Sep 27, 2012 at 10:12 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

Another:

The notes from last week's meeting said WRT function parameter bindings:

Conclusion/Resolution

  • var bindings and are in scope within the function
  • cannot use let to shadow a parameter

In any scope, or just the top scope of the function? Restricting "let" in this fashion seems odd. Does let otherwise have any shadowing restrictions? Not that I expect this to be problematic except when translating code from other languages (either by hand or by a cross compiler).

# Allen Wirfs-Brock (13 years ago)

On Sep 27, 2012, at 1:40 PM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

...

I'm all for statically disallowing many of these "stupid" cases and I think it is fine to do the checks at "first call" rather than load-time (a off-line static linter could be more aggressive in reporting such errors).

No. As a matter using accurate language, that's not "statically".

I generally use "static" to mean: solely based upon analysis of the program text without any dependencies upon runtime state. That's my criteria for classifying something as part of the language's static semantics" (which includes early errors).

All ES errors (including early errors ) end up being reported at runtime. It's only a matter of how much, if any, execution is permitted before such errors are reported.

No doubt there is more to be said about this on the other thread.

More, you didn't respond to my "use strict"; point. Are you going to change whether duplicate parameters with "use strict"; in the function prologue throw an early error, or not?

My default is to make no changes to existing ES5 semantics without careful consideration of the impacts. If we defer reporting of these new errors that we are discussing to a latter point during execution it might be reasonable to also defer reporting of the ES5 "use strict" derived errors. That would presumably be a compatible change as it relaxes an error condition. But first we need to decide whether or not we are going to even have the concept of "deferred early error" .

# Allen Wirfs-Brock (13 years ago)

On Sep 28, 2012, at 7:58 AM, John Lenz wrote:

On Thu, Sep 27, 2012 at 10:12 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: Another:

The notes from last week's meeting said WRT function parameter bindings:

Conclusion/Resolution

  • var bindings and are in scope within the function
  • cannot use let to shadow a parameter

In any scope, or just the top scope of the function? Restricting "let" in this fashion seems odd. Does let otherwise have any shadowing restrictions? Not that I expect this to be problematic except when translating code from other languages (either by hand or by a cross compiler).

Those specific conclusions were WRT function arguments and top level function declarations.

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

I generally use "static" to mean: solely based upon analysis of the program text without any dependencies upon runtime state. That's my criteria for classifying something as part of the language's static semantics" (which includes early errors).

Ok so far.

All ES errors (including early errors ) end up being reported at runtime.

No, here you redefine "runtime" from any useful definition. Of course stages nest or recur fractally but in the browser, if a script loads and the first evaluation in it runs, you've reached "runtime". And ES5 is quite clear that early errors -- including in nested functions, including in strict function, must stop the whole script from reaching runtime.

It's only a matter of how much, if any, execution is permitted before such errors are reported.

You're still dodging the "use strict"; ES5 precedent point.

# Allen Wirfs-Brock (13 years ago)

On Sep 28, 2012, at 9:16 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I generally use "static" to mean: solely based upon analysis of the program text without any dependencies upon runtime state. That's my criteria for classifying something as part of the language's static semantics" (which includes early errors).

Ok so far.

All ES errors (including early errors ) end up being reported at runtime.

No, here you redefine "runtime" from any useful definition. Of course stages nest or recur fractally but in the browser, if a script loads and the first evaluation in it runs, you've reached "runtime". And ES5 is quite clear that early errors -- including in nested functions, including in strict function, must stop the whole script from reaching runtime.

It depends upon whether you are considering the "runtime" of an individual script or the "runtime" of an application. Most ES applications are composed of multiple scripts so the "early" point prior to script runtime for a particular script is still usually somewhere in the middle of the "runtime" of the application.

It's only a matter of how much, if any, execution is permitted before such errors are reported.

You're still dodging the "use strict"; ES5 precedent point.

I said:

# Brendan Eich (13 years ago)

Allen Wirfs-Brock wrote:

On Sep 28, 2012, at 9:16 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

I generally use "static" to mean: solely based upon analysis of the program text without any dependencies upon runtime state. That's my criteria for classifying something as part of the language's static semantics" (which includes early errors). Ok so far.

All ES errors (including early errors ) end up being reported at runtime. No, here you redefine "runtime" from any useful definition. Of course stages nest or recur fractally but in the browser, if a script loads and the first evaluation in it runs, you've reached "runtime". And ES5 is quite clear that early errors -- including in nested functions, including in strict function, must stop the whole script from reaching runtime.

It depends upon whether you are considering the "runtime" of an individual script or the "runtime" of an application.

Clinton-esque. :-/

It depends on nothing. We know how scripts load today, how ES5 mandates early errors. If you have a break to a non-existent/typo'ed label deep in a function nest, the entire containing script fail-stops at compile time without side effects.

This is a useful property, it's not something to throw away or try to equate to function-wise tardy errors.

Most ES applications are composed of multiple scripts so the "early" point prior to script runtime for a particular script is still usually somewhere in the middle of the "runtime" of the application.

Yes, obviously -- but you seem to be equating that with a much finer grained tardy error semantics that litters the heap with miscompiled functions.

I think that's pretty wrong.

It's only a matter of how much, if any, execution is permitted before such errors are reported. You're still dodging the "use strict"; ES5 precedent point.

I said:

On Sep 28, 2012, at 8:46 AM, Allen Wirfs-Brock wrote:

On Sep 27, 2012, at 1:40 PM, Brendan Eich wrote:

More, you didn't respond to my "use strict"; point. Are you going to change whether duplicate parameters with "use strict"; in the function prologue throw an early error, or not? My default is to make no changes to existing ES5 semantics without careful consideration of the impacts. If we defer reporting of these new errors that we are discussing to a latter point during execution it might be reasonable to also defer reporting of the ES5 "use strict" derived errors. That would presumably be a compatible change as it relaxes an error condition.

No, you may break code that today works by a script fail-stopping without effect, intentionally or accidentally. It's an incompatible change and it won't fly.

Moreover, you are not considering the other alternative: add more early errors in ES6 because they are no more onerous than ES5 strict checks already in the field, and they build on sunk costs in those implementations.

The proximate example is duplicated formals in conjunction with new parameter syntax (destructuring, defaults). If I missed a reply from you on this, sorry. It would be good to get agreement on true early errors for duplicated parameter names when mixed with new parameter syntax. Doug, Andreas, and others (I bet Mark) agree.

But first we need to decide whether or not we are going to even have the concept of "deferred early error" .

We do! Where's the list of candidate early error cases? Cc'ing Luke.

# Allen Wirfs-Brock (13 years ago)

On Sep 28, 2012, at 11:23 AM, Brendan Eich wrote:

We do! Where's the list of candidate early error cases? Cc'ing Luke.

Within recent ES6 drafts, all early errors are identified within subsections labeled: "Static Semantics: Early Errors"

The easiest way to see all of them is just to do a search for that subsection label.