Proposal: opt-out local scoping

# Yuh-Ruey Chen (16 years ago)

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

# Dave Herman (16 years ago)

I agree with you 100% that JavaScript /almost/ gets lexical scope right and that we should try to eliminate that "almost" as much as we can. I don't, however, believe that the right solution is Python's implicit binding semantics.

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.

I think you're still going to have the same subtleties as hoisting with this proposal, since all the variables assigned to in the block will be in scope for the whole block; so they'll be `undefined' before being assigned to and there will still be the same closure hazards where you might think the variable was in scope in a nested block but in fact it goes out to the nearest enclosing var { ... } block.

var { ... nonlocal x [ = y]; ... }

For my tastes, this puts too much of a tax on referring to outer variables.

function list(iterable) { var { if (iterable is Array) { lst = iterable; } else { list = [];

I think you mean `lst = []' here?

        for (x in iterable) {
            lst.push(x);
        }
    }
}
return lst;

}

These are the kinds of bugs that I think this semantics would result in: when assignment implicitly binds variables, fat-fingering an assigned variable name silently works and then causes unpredictable behavior later. Whereas with traditional lexical scope, when you declare your variables up front, both assignments and references are checked against the variables in scope, and you get an immediate error -- even statically detectable.

I don't believe that

 var {
     ...
     a = f()
     ...
     b = g()
     ...
     c = h()
     ...
 }

is that much less of a hardship than

 {
     ...
     var a = f()
     ...
     var b = g()
     ...
     var c = h()
     ...
 }

and I believe the benefits of a clearer semantics -- and clearer and earlier errors on assignment to free variables -- are worth it. But I agree that we need to do something to correct the semantics of looking up free variables dynamically.

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

Lexical scope is in the air. :) Please take a look at the lexical scope proposal on the wiki and offer any comments or suggestions:

 http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope

Essentially, the above is a less radical proposal that simply uses the lexical scope that's already there in JavaScript, but as you suggest enforces it with a pragma. The result is a language where free variables, both in assignments and references, are a statically detectable error, but with otherwise essentially the same semantics that JavaScript already has.

# P T Withington (16 years ago)

On 2008-08-28, at 07:52EDT, Dave Herman wrote:

Lexical scope is in the air. :) Please take a look at the lexical scope proposal on the wiki and offer any comments or suggestions:

http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope

Essentially, the above is a less radical proposal that simply uses the lexical scope that's already there in JavaScript, but as you suggest enforces it with a pragma. The result is a language where free variables, both in assignments and references, are a statically detectable error, but with otherwise essentially the same semantics
that JavaScript already has.

I like this, but wouldn't you want to provide escapes, like "reformed
with" and/or a way to declare an individual reference to be free?

# Dave Herman (16 years ago)

I like this, but wouldn't you want to provide escapes, like "reformed with" and/or a way to declare an individual reference to be free?

Reformed with' depended on type annotations and structural type syntax, which are undergoing discussion. So I think reformedwith' is up in the air for now.

As for free references, what can you do with a free variable? If you mean you want a way to look something up in the global object, then use this.id' orthis[expr]' (or `let global = this' followed by global.id/global[expr]).

It might be nice to have a standard library (called global' or something) that's bound to the global object so you can have a less fragile binding to the global object thanthis'.

# P T Withington (16 years ago)

On 2008-08-28, at 09:09EDT, Dave Herman wrote:

As for free references, what can you do with a free variable? If you
mean you want a way to look something up in the global object, then
use this.id' orthis[expr]' (or `let global = this' followed by
global.id/global[expr]).

It might be nice to have a standard library (called global' or something) that's bound to the global object so you can have a less fragile binding to the global object thanthis'.

Exactly. I think it would be worthwhile to have a standard way to
refer to the global object.

# Erik Arvidsson (16 years ago)

I hope we can still have "global" in Harmony. It is ugly to have to do:

const global = this;

and rely on that no one moves that code into a closure with a different "this".

Dave, I really like the lexical scope proposal. However, I do find the sections regarding the global a bit confusing. Are the following assumptions correct?

this.print = function(s) { ... };

{ use lexical scope; this.print('OK'); print('FAIL'); }

# ihab.awad at gmail.com (16 years ago)

On Thu, Aug 28, 2008 at 9:44 AM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:

I hope we can still have "global" in Harmony. It is ugly to have to do: const global = this; and rely on that no one moves that code into a closure with a different "this".

For what value of "global" should the "global" keyword be global? };->

If you are inside some separately loaded compilation unit, does "global" mean global to the module?

Also, you seem to be asking for an explicit way to say, "this variable does not respect Tennent correspondence; I know I am doing this and I have a good reason" --

gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

What's the use case/good reason?

Ihab

# Yuh-Ruey Chen (16 years ago)

Dave Herman wrote:

I think you're still going to have the same subtleties as hoisting with this proposal, since all the variables assigned to in the block will be in scope for the whole block; so they'll be `undefined' before being assigned to and there will still be the same closure hazards where you might think the variable was in scope in a nested block but in fact it goes out to the nearest enclosing var { ... } block.

Actually, I forgot to mention that in there should no longer be any hoisting in this case; it should be a syntax error:

blah = 10; var { print(blah); blah = 20; print(blah); }

The first |print(blah)| should be a syntax error. I think this is what Python does too.

var { ... nonlocal x [ = y]; ... }

For my tastes, this puts too much of a tax on referring to outer variables.

The alternative, which Pythonites have used for a while until the advent of |nonlocal|, was to have an object/array in the outer scope and have the outer variable be a property of that object:

var { function outer() { x = 10; a = [x]; function inner() { return a[0]; } return inner(); } }

However, this is pretty ugly and isn't very discoverable.

These are the kinds of bugs that I think this semantics would result in: when assignment implicitly binds variables, fat-fingering an assigned variable name silently works and then causes unpredictable behavior later. Whereas with traditional lexical scope, when you declare your variables up front, both assignments and references are checked against the variables in scope, and you get an immediate error -- even statically detectable.

While this is true, this is far less of a problem than opt-in local scoping, because the errors with opt-out local scoping are always going to be local to the block/function the variable was assigned in.

Because of this, I believe the convenience is worth the cost.

I don't believe that

 var {
     ...
     a = f()
     ...
     b = g()
     ...
     c = h()
     ...
 }

is that much less of a hardship than

 {
     ...
     var a = f()
     ...
     var b = g()
     ...
     var c = h()
     ...
 }

and I believe the benefits of a clearer semantics -- and clearer and earlier errors on assignment to free variables -- are worth it. But I agree that we need to do something to correct the semantics of looking up free variables dynamically.

I don't particularly like |var { ... }| myself, but it's the only other way besides a pragma - I would much prefer a pragma, which would pretty much eliminate the usage of |var|.

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

Lexical scope is in the air. :) Please take a look at the lexical scope proposal on the wiki and offer any comments or suggestions:

 http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope

Essentially, the above is a less radical proposal that simply uses the lexical scope that's already there in JavaScript, but as you suggest enforces it with a pragma. The result is a language where free variables, both in assignments and references, are a statically detectable error, but with otherwise essentially the same semantics that JavaScript already has.

Dave

I've taken a look at that, and while it does address free variables, I'd still rather not have to sprinkle redundant |var|s everywhere.

# Erik Arvidsson (16 years ago)

global as speced for ES4 was equivalent to this in global scope.

There are several use cases for having global. Most of them involves some kind of reflection. For example finding all global functions that start with 'test' or finding out if there is an object called 'google.gears.factory'. Another common case that we use a lot is to export a named property to the global scope:

global['foo'] = innerFoo;

# Dave Herman (16 years ago)

Dave, I really like the lexical scope proposal. However, I do find the sections regarding the global a bit confusing. Are the following assumptions correct?

If we assume there is no standard library function called `print'...

this.print = function(s) { ... };

Then this dynamically adds a property `print' to the global object...

{ use lexical scope; this.print('OK');

This dynamically looks up the `print' property in the global object and succeeds...

print('FAIL');

This statically looks up `print' in the lexical environment and fails.

}

If that's what you meant, then yes.

# Dave Herman (16 years ago)

For what value of "global" should the "global" keyword be global? };->

I'm not sure if this is what Erik meant, but my intention was just that there's a variable bound in the standard library called global' that's bound to the global object. The only reason I suggested the nameglobal' was that the spec uses the terminology "the global object." But there's nothing special about the variable; it can be shadowed like any other variable.

I interpreted Erik's point to be that the binding of `this' is not lexically scoped, so it would be useful to have a lexically scoped variable initially bound to the global object. IOW, if I write:

 this.print("blah blah blah")

and then I refactor the code to say:

 (function() {
     this.print("blah blah blah")
 })()

it breaks. By contrast if I have a standard library binding global' that's bound to the same thing asthis' at the top level, then I can write:

 global.print("blah blah blah")

and the same refactoring:

 (function() {
      global.print("blah blah blah")
 })()

continues to work the same. But there's no need for a special keyword or anything like that.

# ihab.awad at gmail.com (16 years ago)

On Thu, Aug 28, 2008 at 12:14 PM, Dave Herman <dherman at ccs.neu.edu> wrote:

For what value of "global" should the "global" keyword be global? };->

I interpreted Erik's point to be that the binding of `this' is not lexically scoped, so it would be useful to have a lexically scoped variable initially bound to the global object. IOW, if I write:

global.print("blah blah blah")

and the same refactoring:

(function() { global.print("blah blah blah") })()

continues to work the same.

Cool. Would there be a 'global' for each module (for some interpretation of "module" but assuming each module has its own separate top-level lexical scope, as appears to be the growing concensus)?

But there's no need for a special keyword or anything like that.

Would that really satisfy Erik's use case? He seemed to think that doing, at the top level --

var global = this;

function foo() { global.bar = 3; }

is vulnerable to some ${person} going --

function foo() { var global = /* something else / global.bar = 3; / now not the real global; system fails! */ }

?

Ihab

# Brendan Eich (16 years ago)

On Aug 28, 2008, at 10:36 AM, Yuh-Ruey Chen wrote:

I've taken a look at that, and while it does address free
variables, I'd still rather not have to sprinkle redundant |var|s everywhere.

Those vars are helpful to some readers, who otherwise have to learn
the Python/Ruby/TCL model and look for prior nonlocals. Programmers
repeat var in unrelated paragraphs precisely to help the "spot
reader" (including themselves, when rearranging code as Ingvar
pointed out).

I'm not criticizing the opt-out local scoping approach. Indeed, Bill
Joy tried too late in 1995 to get me to switch JS to use it, without
a pragma -- just an incompatible change. At that point there was
already too much content depending on it (Marc Andreessen made the
same point about Mosaic and NCSA httpd already having "too many" --
80 or so -- sites, hosting quirky HTML that motivated error- corrections in the Mosaic parser).

Between the utility of var for some readers and writers today, and
the closer concept of pure lexical scope that Dave proposes, there is
less distance than there is between today's JS and an opt-out local
scoping regime.

This distance argument is important both for the language spec, to
conserve concepts, and for programmers who have to read and write
code reused (read and extended) by other programmers. Adding an opt- out local scoping mechanism that sees wide adoption would split the
world of JS programs in two. I don't think it would take over
completely, ever, so it would leave a gulf between the compatible
scoping model and the opt-out local scoping model.

Again I'm not bike-shedding the details of your proposal or knocking
the virtue of opt-out local scoping in other languages. I'm pointing
out that we can't go back to 1995 and change JS to have a single
better scoping system. Given the need for a pragmatic way to change
the scoping model, resulting in two models in practice if the new
way sees adoption, I am in favor of a shorter-distance proposal that
does less violence to the habits of JS hackers.

Beyond this point, lexical scope actually reforms the global object
by getting it out of the scope chain. And it allows global bindings
that people want to count on (Date, alert, document) to become
lexical, possibly even immutable depending on how we do it.

# Dave Herman (16 years ago)

Cool. Would there be a 'global' for each module (for some interpretation of "module" but assuming each module has its own separate top-level lexical scope, as appears to be the growing concensus)?

My intention was for the use lexical scope' mini-proposal to be orthogonal to any module proposal. This would be a design question for modules, not foruse lexical scope'.

I wouldn't treat global' as anything special, nothing more than part of the standard library. To that end, one module proposal might start modules out in a completely empty environment, where you could import the standard library explicitly (which would includeglobal'); another module proposal might start modules out in a default environment that contains the standard library (which would include `global'). But for various reasons I'm not ready to start discussing modules with people.

Would that really satisfy Erik's use case? He seemed to think that doing, at the top level --

var global = this;

function foo() { global.bar = 3; }

is vulnerable to some ${person} going --

function foo() { var global = /* something else / global.bar = 3; / now not the real global; system fails! */ }

You can make the same case for any variable. This is just lexical scope in action. The meaning of a variable reference is its innermost lexical binding. If you shadow a variable, it's shadowed.

Erik, was this your concern?

# Brendan Eich (16 years ago)

On Aug 28, 2008, at 12:14 PM, Dave Herman wrote:

I interpreted Erik's point to be that the binding of `this' is not lexically scoped, so it would be useful to have a lexically scoped variable initially bound to the global object. IOW, if I write:

 this.print("blah blah blah")

and then I refactor the code to say:

 (function() {
     this.print("blah blah blah")
 })()

it breaks.

It happens not to break, because ES3 requires a null |this| for such
calls, where the null is later replaced by the global object.

obj = { method: function () { this.print("blah blah blah"); }, print: function () { print("not the print you want"); } }; obj.method();

would break, though.

We've been trying to fix the ES3 null->global rule for a while. Any

change is an incompatible change, but the current rule leads to
unintended global mutation and capture bugs.

By contrast if I have a standard library binding global' that's bound to the same thing asthis' at the top level, then I
can write:

 global.print("blah blah blah")

and the same refactoring:

 (function() {
      global.print("blah blah blah")
 })()

continues to work the same. But there's no need for a special
keyword or anything like that.

Indeed, Doug Crockford proposed at the January TC39 meeting this year
to make 'this' act like a lexically bound variable, with the only
magic to it applying to the case of obj.method() call expressions
(and variatons, obj[name] where name = 'method'), where 'this' would
be overridden -- if you will, a shadowing 'this' would be bound to obj.

I liked Doug's proposal quite a bit. I do not see anything like it in
ES3.1, but I'd like to see it in Harmony.

# P T Withington (16 years ago)

On 2008-08-28, at 15:47EDT, Brendan Eich wrote:

Indeed, Doug Crockford proposed at the January TC39 meeting this
year to make 'this' act like a lexically bound variable, with the
only magic to it applying to the case of obj.method() call
expressions (and variatons, obj[name] where name = 'method'), where
'this' would be overridden -- if you will, a shadowing 'this' would
be bound to obj.

I liked Doug's proposal quite a bit. I do not see anything like it
in ES3.1, but I'd like to see it in Harmony.

I'd like to know more about the proposal, since I can't figure out
what "make 'this' act like a lexically bound variable" means.

I'd like to have a syntax where this is not implicitly bound. One
idea would be to riff on default arguments:

function example (receiver=this, ...

to rename the implicit -1th argument to function calls.

# Brendan Eich (16 years ago)

On Aug 28, 2008, at 1:23 PM, P T Withington wrote:

I'd like to have a syntax where this is not implicitly bound. One idea would be to riff on default arguments:

function example (receiver=this, ...

Why wouldn't you use |this| for the parameter name? We did for type- annotating |this| in various proposals.

Some functions have a bound |this| when you extract them. Such
override attempts would fail. Throw or fail silently?

# Erik Arvidsson (16 years ago)

On Thu, Aug 28, 2008 at 12:29, Dave Herman <dherman at ccs.neu.edu> wrote:

Would that really satisfy Erik's use case? He seemed to think that doing, at the top level --

var global = this;

function foo() { global.bar = 3; }

is vulnerable to some ${person} going --

function foo() { var global = /* something else / global.bar = 3; / now not the real global; system fails! */ }

You can make the same case for any variable. This is just lexical scope in action. The meaning of a variable reference is its innermost lexical binding. If you shadow a variable, it's shadowed.

Erik, was this your concern?

I'm not concerned about someone shadowing it. My concern was with cases like the one Brendan pointed out as well as code snippets like:

new function() { var global = this; global.print(124); }

Some people tend to use that format over (function() { })() and wrapping code with that construct would break the global reference.

# Ingvar von Schoultz (16 years ago)

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.

Radical? I find my proposal far less radical than, for example, the Java classes discussed elsewhere.

With Java classes, a paradigm is chosen for you. My sugar embraces the many paradigms of JavaScript. You get clear syntax for many different paradigms.

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.

It's a very annoying drawback. But despite this I'm not convinced about your proposal. If instead undeclared names are forbidden, this is so very helpful in debugging that it trumps any other arrangement. Although your automatic scoping (with some improvements) would be useful for many users, I'm not convinced that it's useful enough, when forbidden undeclared are so much more helpful.

However I'd love to see |outer| (which I prefer over |nonlocal|) required for every variable outside the current function (not block). That would be a great debugging help:

 var x, y, z;
 function f()
 {   outer x;
     var a = x;
     var b = outer y;
     var c = y;
     var d = z; // Error.
 }

So here's the proposal that is backwards compatible: provide a block that changes the "default" scoping.

var { ... }

The syntax would have to be different, because that looks like the start of a destructuring assignment.

# Ingvar von Schoultz (16 years ago)

Dave Herman wrote:

Reformed with' depended on type annotations and structural type syntax, which are undergoing discussion. So I think reformedwith' is up in the air for now.

I find it odd that reformed |with| required such exact type annotations, when nothing else does. It would seem that these are very similar, from an early-binding viewpoint:

 var {a, b, c} = fn();
 x = a;

 var obj = fn();
 x = obj.a;
 with (obj : {a, b, c})
 {   x = a; // Desugars to obj.a
 }

Equally it would seem that disambiguating syntax would allow early binding, if it were enforced:

 with (obj)
 {   x = .a; // Desugars to obj.a
     y = a;  // From the lexical scope
 }
# Brendan Eich (16 years ago)

On Aug 28, 2008, at 2:59 PM, Ingvar von Schoultz wrote:

Dave Herman wrote:

Reformed with' depended on type annotations and structural type syntax, which are undergoing discussion. So I think reformedwith' is up
in the air for now.

I find it odd that reformed |with| required such exact type annotations, when nothing else does.

Please read

proposals:reformed_with

the last paragraph in particular.

It would seem that these are very similar, from an early-binding viewpoint:

 var {a, b, c} = fn();
 x = a;

 var obj = fn();
 x = obj.a;
 with (obj : {a, b, c})
 {   x = a; // Desugars to obj.a
 }

Your example includes neither type constraints nor mutation.

Equally it would seem that disambiguating syntax would allow early binding, if it were enforced:

 with (obj)
 {   x = .a; // Desugars to obj.a

New syntax could solve the ambiguity problem, but not the type
variance problem.

# P T Withington (16 years ago)

On 2008-08-28, at 16:52EDT, Brendan Eich wrote:

On Aug 28, 2008, at 1:23 PM, P T Withington wrote:

I'd like to have a syntax where this is not implicitly bound. One idea would be to riff on default arguments:

function example (receiver=this, ...

Why wouldn't you use |this| for the parameter name? We did for type- annotating |this| in various proposals.

In a function (that is not a method of a class), forcing me to call
the -1th parameter of the function 'this' seems Procrustean.

Some functions have a bound |this| when you extract them. Such
override attempts would fail. Throw or fail silently?

I'm just asking that I be able to use a name other than 'this' for my
-1th parameter, should I so choose; especially in functions outside of
a class. The magic binding of the -1th parameter of a function inside
a class to the instance it was extracted from should be independent of
the name of that parameter.

# Felix (16 years ago)

ihab.awad at gmail.com wrote:

Also, you seem to be asking for an explicit way to say, "this variable does not respect Tennent correspondence; I know I am doing this and I have a good reason" --

gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

I'm suspicious of Tennent correspondence, because I think simple variations of the statement do not match anyone's expectations.

if I understand correctly, Tennent correspondence says:

t1: any expr should be equivalent to (function() {expr}) ()

and one of the difficulties is, what if expr has control-flow statements, such as break or return. another difficulty is, what if expr has lexically-scoped statements, such as local variable declarations.

assume N is a unique generated symbol. Tennant correspondence could also be stated as

t2: any expr should be equivalent to a function in the same scope function N() {expr} and a call to that function N()

that isn't true in any practical language I know, and it seems unhelpful to say that function declaration is broken in all of them. I don't think anyone expects that you can extract arbitrary pieces of code into a separate function without fixing up control-flow and scoping.

and part of the usefulness of functions is that they do introduce a discontinuity, which lets you express things that are more difficult to express without it.

# Mark S. Miller (16 years ago)

On Fri, Aug 29, 2008 at 6:45 PM, Felix <felix8a at gmail.com> wrote:

ihab.awad at gmail.com wrote:

Also, you seem to be asking for an explicit way to say, "this variable does not respect Tennent correspondence; I know I am doing this and I have a good reason" --

gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

I'm suspicious of Tennent correspondence, because I think simple variations of the statement do not match anyone's expectations.

if I understand correctly, Tennent correspondence says:

t1: any expr should be equivalent to (function() {expr}) ()

and one of the difficulties is, what if expr has control-flow statements, such as break or return. another difficulty is, what if expr has lexically-scoped statements, such as local variable declarations.

Since ES is not an expression language, it's useful to divide the question into

  • TC for expressions expr equiv (function(){return expr;})() ES violations: this, arguments. 'this' breakage can possibly be addressed with '.bind(this)' as in my previous message. expr equiv (function(){return expr;}).call(this)

  • TC for blocks {stats} equiv (function(){stats})(); Same issues re this, arguments Additional ES violations: return, break, continue, var hoisting

Note: The above litany of problems are not fixable in ES. But we should try to avoid digging this hole any deeper.

assume N is a unique generated symbol. Tennant correspondence could also be stated as

t2: any expr should be equivalent to a function in the same scope function N() {expr} and a call to that function N()

that isn't true in any practical language I know,

Smalltalk, Scheme, Self, E, AmbientTalk/2, and apparently Scala. In general, I'd expect it to be true in any language in which closures are used to do all control abstraction, which probably includes a large number of languages I haven't mentioned. (Note: E currently has one minor TC violation which we consider to be a bug and plan to fix.)

and it seems unhelpful to say that function declaration is broken in all of them. I don't think anyone expects that you can extract arbitrary pieces of code into a separate function without fixing up control-flow and scoping.

and part of the usefulness of functions is that they do introduce a discontinuity, which lets you express things that are more difficult to express without it.

Smalltalk, Scheme (at least prior to R6RS), and Self are widely and properly regarded as among the least broken programming languages that have ever been designed. The early JavaScript set us on a path worth rescuing largely due to Brendan's good taste in appreciating the virtues of Scheme and Self.

# Waldemar Horwat (16 years ago)

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 (16 years ago)

Yes, Dave Herman already mentioned that, and I replied with this:

While this is true, this is far less of a problem than opt-in local scoping, because the errors with opt-out local scoping are always going to be local to the block/function the variable was assigned in.

Because of this, I believe the convenience is worth the cost.