Proposal: opt-out local scoping

Waldemar Horwat waldemar at google.com
Fri Sep 5 12:32:24 PDT 2008


With this proposal you just get the dual problem:  You think you're assigning to an outer-scope variable while in fact you're creating and later discarding a local variable.  This would just flip the confusion you worry about to the other case without eliminating it.

ES proposals already provide a good solution to this problem:  strict mode.  In strict mode you can't accidentally create a global just because you have a missing var.  You get an error if you try.

    Waldemar


Yuh-Ruey Chen wrote:
> Well we're all discussing radical new syntaxes (I'm looking at you,
> Ingvar), I might as well propose my own.
> 
> One thing about JavaScript that has always really bothered me (and
> probably several of you) is how JavaScript has opt-in local scope,
> rather than opt-out local scope. That is, in order to have an identifier
> reference a local variable, there needs to be a |var| declaration that
> says that that identifier refers to a local variable, and not non-local
> one (either in a surrounding function or the global scope). This has a
> couple related problems:
> 
> 1) Lack of integrity - in that it's easy to shoot yourself in the foot.
> One missing |var| and you can be debugging some random error in the
> wrong place.
> 
> 2) It's inconvenient. This is pretty self-explanatory. It also
> encourages the usage of redundant var declarations in
> often-copied/pasted code, even if it may not be necessary due to var
> hoisting, which is apparently a practice some people here are not fond of.
> 
> 3) It can be confusing due to var hoisting. Suppose you have code like this:
> 
> blah = 10;
> (function() {
>     print(blah);
>     var blah = 20;
>     print(blah);
> })();
> 
> The novice user would expect this to print "10" followed by "20", when
> it really prints out "undefined" followed by "20".
> 
> This has all been discussed to death before, I'm sure, with the
> conclusion that changing the behavior is backwards incompatible. And I
> vaguely remember someone saying that we shouldn't add a pragma to "fix"
> this issue, although the reason for that escapes me at the moment.
> 
> 
> So here's the proposal that is backwards compatible: provide a block
> that changes the "default" scoping.
> 
>     var { ... }
> 
> where everything assigned in ... results in a variable local to that
> block. Note that this only affects _assignment_. You can still read from
> a variable from a surrounding scope.
> 
> As some extra sugar,
> 
>     function name(args) var { ... }
> 
> would desugar
> 
>     function name(args) { var { ... } }
> 
> The default scoping setting also nests, i.e. it also crosses function
> boundaries, so in
> 
> var {
>     function name(args) { ... }
> }
> 
> everything assigned in ... results in a variable local to the functions
> block.
> 
> To escape (opt out) of the scoping setting, we need a new keyword.
> Python 2.5 uses the keyword |nonlocal| for this purpose, so I'll use it
> as well as a placeholder.
> 
> var {
>     ...
>     nonlocal x [ = y];
>     ...
> }
> 
> Finally, we can do the same thing for |let|:
> 
> let { ... }
> 
> etc.
> 
> Some examples:
> 
> ---
> 
> function list(iterable) {
>     var {
>         if (iterable is Array) {
>             lst = iterable;
>         } else {
>             list = [];
>             for (x in iterable) {
>                 lst.push(x);
>             }
>         }
>     }
>     return lst;
> }
> 
> function stats(iterable) var { // <-- notice var
>     lst = list(iterable);
>    
>     function calcSum() {
>         sum = 0;
>         for each (x in lst) {
>             sum += x;
>         }
>     }
>    
>     sum = calcSum();
>    
>     var mean;
>    
>     function calcMean() {
>        nonlocal mean;
>        mean = sum / lst.length;
>     }
>    
>     return [lst.length, sum, mean];
> }
> 
> ---
> 
> which desugars to:
> 
> ---
> 
> function list(iterable) {
>     var lst;
>     if (iterable is Array) {
>         lst = iterable;
>     } else {
>         list = [];
>         for (var x in iterable) {
>             lst.push(x);
>         }
>     }
>     return lst;
> }
> 
> function stats(iterable) {
>     var lst = list(iterable);
>    
>     function calcSum() {
>         var sum = 0;
>         for each (var x in lst) {
>             sum += x;
>         }
>     }
>    
>     var sum = calcSum();
>     var mean = sum / lst.length;
>     return [lst.length, sum, mean];
> }
> 
> ---
> 
> And finally, it would be nice to have a pragma that can do this for us
> (again, I don't recall the argument against them). Something like:
> 
>     use scope var;
>     use scope let;
>     use scope nonlocal; // default for backwards compatibility
> 
> which would obviate the need to add all these |var { ... }| and |let {
> ... }| statements in new code.
> 
> For example, the following would be equivalent to the above examples:
> 
> ---
> 
> use scope var;
> 
> function list(iterable) {
>     if (iterable is Array) {
>         lst = iterable;
>     } else {
>         list = [];
>         for (x in iterable) {
>             lst.push(x);
>         }
>     }
>     return lst;
> }
> 
> function stats(iterable) {
>     lst = list(iterable);
>    
>     function calcSum() {
>         sum = 0;
>         for each (x in lst) {
>             sum += x;
>         }
>     }
>    
>     sum = calcSum();
>    
>     var mean;
>    
>     function calcMean() {
>        use scope nonlocal;   // notice that these pragmas can be local
> to blocks
>        mean = sum / lst.length;
>     }
>    
>     return [lst.length, sum, mean];
> }
> 
> ---
> 
> I should also point out that this is not a radical new concept. Both
> Ruby and Python have had this functionality for a while. Comments?
> 
> -Yuh-Ruey Chen
> _______________________________________________
> Es-discuss mailing list
> Es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
> 



More information about the Es-discuss mailing list