Generator issue: exceptions while initializing arguments

Brendan Eich brendan at mozilla.com
Wed Sep 12 04:47:51 PDT 2012


Kevin Smith wrote:
>
>     function f(x) {
>         console.log(x);
>         if (truthy()) {
>           with ({x: 'ha ha'}) {
>             var x = 'ho ho';
>             console.log(x);
>           }
>         }
>         function x(){}
>             console.log(x);
>
>       }
>
>     In these cases there is information flowing right to left, even
>     across unbalanced left brace!
>
>
> You're not seriously arguing for how new things should work based on 
> "with" and "arguments" are you?  : )

No arguments object usage here, and takea way the 'with' and you still 
have hoisting to contend with, even across a (non-body) left brace if 
the var is nested.

My point is serious, in that we are not bound to follow C, C++, or Java 
and try to separate formal parameter scope, default parameter 
initialization, or other observables from those languages, just because 
of that left curly brace at the start of a function body.

>
>     This is JS. We can't change it. Are default parameters really the
>     place to make a new last stand for something different?
>
>
> Maybe not.  It may not be worthwhile to try and maintain the "curly 
> law" here, but just as a thought experiment:
>
>     function g() { return "outer"; }
>     function f(a = g()) { return a; function g() { return "inner"; } }

Function hoisting is what it is, so putting nested function g's 
declaration after the return statement shouldn't be relevant to us in 
this discussion. You could as well have put it before the return. Just 
sayin' ;-).

>     console.log(f());
>
> What should we see logged?  If we're inclined to think that curlies 
> should define scope boundaries, then we might answer "outer".  In 
> order to make that happen, we'd need to introduce a new scope in 
> between f's declaration and f's body:
>
>     {0: g, f {1: a } {2: a, g } }
>
> where 0 is the outer scope, 1 is the scope of parameter default 
> expressions, and 2 is the function body scope.  This model isn't 
> conceptually clean either though, because we have weird "copy-like" 
> semantics as parameter values "teleport" from scope 1 to scope 2.  Meh.

Yup.

> If both scoping semantics (Jason's "top of function body" and the one 
> above) are awkward for different reasons, then maybe there's a better 
> way to approach default values.
>
> A while ago existential and default operators were discussed, but I 
> didn't pay much attention.  If we have a default operator, then we 
> could cut default parameter values altogether:
>
>     function f(a = g()) {
>       return a;
>       function g() {}
>     }
>
>     // is the same as:
>
>     function f(a) {
>       a SOME_DEFAULT_OPERATOR g;
>       return a;
>       function g() {}
>     }
>
> It's not quite as pretty as parameter defaults, but it doesn't suffer 
> from the scope awkwardness, either.

We don't have default operators in ES6 but we do have default 
parameters, which address the most frequently seen use-case. Flipping it 
around at this point is bad for two reasons:

* we have to escalate default operator out of strawman and undo the 
default parameter work;

* more significant: we have less user testing and frequent-use-case 
evidence in favor of the default operator approach;

* the default operator approach is verbose, restating the parameter name 
on the LHS.

>  As an added bonus the loc1 vs. loc2 issue that started the thread 
> basically goes away.

No, I think not -- that was about the implicit yield in generators, and 
a red herring, I think. The real issue is the scope of default 
parameters, as you say.

/be


More information about the es-discuss mailing list