const initializers: once more unto the breach

# Andy Wingo (14 years ago)

Hello,

Unifying the specifications of let and const in the latest draft means that now, const statements can have no initializer. Is that intended?

Beyond that, const is just a bit strange. For example:

(function (){ baz = 20; return baz; const baz = 30; baz = 40; baz = 50; })() => 20

Or:

(function (){ const baz; return function(x) { baz = x; return function() { return baz; } }; })(20)() => 20

I don't really get the point of const, to be honest. It doesn't help implementors, because the compiler already knows if a variable is assigned more than once.

It doesn't seem to help programmers, because the delayed initialization with temporal dead zone means that you might have to do just as much mental work to figure out what sets a const var as before. Seeing "const baz;" is pretty strange.

Ignorant suggestion: either require initializers, and disallow assignment to const variables outside an initializer, or remove const from the language.

,

Andy

# Andy Wingo (14 years ago)

On Tue 20 Mar 2012 16:45, Andy Wingo <wingo at igalia.com> writes:

Unifying the specifications of let and const in the latest draft means that now, const statements can have no initializer. Is that intended?

Of course, after sending this mail, I find:

Static Semantics: Early Errors
LexicalBinding : BindingIdentifier

It is a Syntax Error if the BindingIdentifier if IsConstantDeclaration of the LexicalDeclaration containing
this production is true.

Some of the const strangeness is still there though. It's not necessarily the case that the value of the const is either nothing (in which case you get a TDZ error) or the result of the initializer -- anything else that executes before the const could set it.

So a revised ignorant suggestion on my part:

disallow assignment to const variables outside an initializer

,

Andy

# Allen Wirfs-Brock (14 years ago)

On Mar 20, 2012, at 8:55 AM, Andy Wingo wrote:

On Tue 20 Mar 2012 16:45, Andy Wingo <wingo at igalia.com> writes:

Your original post doesn't seem to have made it to the discussion list.

Unifying the specifications of let and const in the latest draft means that now, const statements can have no initializer. Is that intended?

Of course, after sending this mail, I find:

Static Semantics: Early Errors LexicalBinding : BindingIdentifier

It is a Syntax Error if the BindingIdentifier if IsConstantDeclaration of the LexicalDeclaration containing this production is true.

there is a typo in that. "if the BindingIdentifier" should be deleted

Some of the const strangeness is still there though. It's not necessarily the case that the value of the const is either nothing (in which case you get a TDZ error) or the result of the initializer -- anything else that executes before the const could set it.

No, because 10.2.1.1.3 would throw on any attempt to assign to a immutable binding created for a const (see 10.5.3, 11.a.ii.1.

So a revised ignorant suggestion on my part:

disallow assignment to const variables outside an initializer

they are, dynamically. Not all variable references are statically resolvable.

Were there any other issues in the original post?

bugs.ecmasript.org is probably a better place for reporting these shorts of issues.

# Andy Wingo (14 years ago)

On Tue 20 Mar 2012 21:13, Allen Wirfs-Brock <allen at wirfs-brock.com> writes:

On Mar 20, 2012, at 8:55 AM, Andy Wingo wrote:

On Tue 20 Mar 2012 16:45, Andy Wingo <wingo at igalia.com> writes:

Your original post doesn't seem to have made it to the discussion list.

Odd. Here's the meat:

Const is just a bit strange.  For example:

  (function (){
     baz = 20;
     return baz;
     const baz = 30;
     baz = 40;
     baz = 50;
   })()
   => 20

Or:

  (function (){
     return function(x) { baz = x; return function() { return baz; } };
     const baz = 30;
   })(20)()
   => 20

I don't really get the point of const, to be honest.  It doesn't help
implementors, because the compiler already knows if a variable is
assigned more than once.

It doesn't seem to help programmers, because the delayed initialization
with temporal dead zone means that you might have to do just as much
mental work to figure out what sets a const var as before.  Seeing
"const baz;" is pretty strange.

Ignorant suggestion: either disallow assignment to const variables
outside an initializer, or remove const from the language.

bugs.ecmasript.org is probably a better place for reporting these shorts of issues.

OK, thanks.

,

Andy

# Allen Wirfs-Brock (14 years ago)

On Mar 20, 2012, at 1:23 PM, Andy Wingo wrote:

On Tue 20 Mar 2012 21:13, Allen Wirfs-Brock <allen at wirfs-brock.com> writes:

On Mar 20, 2012, at 8:55 AM, Andy Wingo wrote:

On Tue 20 Mar 2012 16:45, Andy Wingo <wingo at igalia.com> writes:

Your original post doesn't seem to have made it to the discussion list.

Odd. Here's the meat:

Const is just a bit strange. For example:

 (function (){
    baz = 20;
    return baz;
    const baz = 30;
    baz = 40;
    baz = 50;
  })()
  => 20

no, throws on baz=20; because assignment to an immutable binding

Or:

 (function (){
    return function(x) { baz = x; return function() { return baz; } };
    const baz = 30;
  })(20)()
  => 20

the baz = x will throw because of assignment to an immutable binding

In these cases, it is possible to detect the assignment to these consts at "compile" time and they could be treated as early errors. However, this isn't possible in all circumstances because of the with statement and non-strict mode evals so the mechanisms we have to have the mechanisms in the spec. for dynamically checking and those mechanism also take care of the statically detectable cases.

It is an open issue whether an implementation should be permitted/required/forbidden to report the statically detectable cases as early errors.

I don't really get the point of const, to be honest. It doesn't help implementors, because the compiler already knows if a variable is assigned more than once.

It doesn't seem to help programmers, because the delayed initialization with temporal dead zone means that you might have to do just as much mental work to figure out what sets a const var as before. Seeing "const baz;" is pretty strange.

Ignorant suggestion: either disallow assignment to const variables outside an initializer, or remove const from the language.

that's how it is spec'ed

# Andy Wingo (14 years ago)

On Tue 20 Mar 2012 21:46, Allen Wirfs-Brock <allen at wirfs-brock.com> writes:

 (function (){
    baz = 20;
    return baz;
    const baz = 30;
    baz = 40;
    baz = 50;
  })()
  => 20

no, throws on baz=20; because assignment to an immutable binding

Ah, I see. Thanks for being patient with my ignorance, and I'll try to be more diligent next time :)

# Andreas Rossberg (14 years ago)

On 20 March 2012 21:46, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

It is an open issue whether an implementation should be permitted/required/forbidden to report the statically detectable cases as early errors.

The obvious and (and IMO right) rule is that static checks should be required iff the declaration is in strict code -- which generally implies that the use site and any scope in-between is strict code, too. So it can be statically checked in all those cases. In sloppy mode, you're on your own as usual.

The only potential problem I can foresee with that is the current strict mode loop hole with indirect 'eval' in combination with const declarations in the global scope, depending on how exactly we will decide on global scope semantics. (That is, we should try to find a semantics that maintains the above invariant.)

# Brendan Eich (14 years ago)

There are still temporal dead zone cases that can't be statically analyzed -- at least not without an aggressive higher-order control flow analysis, and even then analysis is approximate so we'd have to mandate false positives or bail to dynamic checks.

Isn't the issue that strict mode would dynamically check these in some implementations, and statically check them in others?

# Andreas Rossberg (14 years ago)

On 21 March 2012 17:47, Brendan Eich <brendan at mozilla.org> wrote:

There are still temporal dead zone cases that can't be statically analyzed -- at least not without an aggressive higher-order control flow analysis, and even then analysis is approximate so we'd have to mandate false positives or bail to dynamic checks.

Isn't the issue that strict mode would dynamically check these in some implementations, and statically check them in others?

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour. (I probably cut too much context from the quote.)

Checking TDZ behaviour is a different story. It's undecidable in general, so we should better just leave it alone.

# Brendan Eich (14 years ago)

Andreas Rossberg wrote:

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour.

Agreed, absolutely. In all-strict code we know all the bindings, we see all assignments.

# Allen Wirfs-Brock (14 years ago)

On Mar 21, 2012, at 10:11 AM, Brendan Eich wrote:

Andreas Rossberg wrote:

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour.

Agreed, absolutely. In all-strict code we know all the bindings, we see all assignments.

Yes, but just because a function is strict doesn't mean that everything is strict:

{const c=0; with (someObj) { (function() {"use strict"}; c=2})(); } }

I prefer algorithmic specifications over prose but I think it would add too much complexity to the spec. to algorithmically specify when an early error must be reported on assignments that may target const bindings. In this case I think the best we can do will be something like: If it can be statically proven prior to execution that an assignment will always result in an ReferenceError the error may be reported as an early error.

However, this decrease interpretability, sone implementation might not report early errors and the program might run successfully on some implementation (with some data) and not run at all on others.

If we want to require an early error then we probably need to specify the analysis that needs to be done. Other wise, some implementations will only early error on the easy cases

# Andreas Rossberg (14 years ago)

On 21 March 2012 20:05, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Mar 21, 2012, at 10:11 AM, Brendan Eich wrote:

Andreas Rossberg wrote:

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour.

Agreed, absolutely. In all-strict code we know all the bindings, we see all assignments.

Yes, but just because a function is strict doesn't mean that everything is strict:

{const c=0; with (someObj) { (function() {"use strict"}; c=2})(); } }

Note that I specifically said that the respective declaration has to be in strict code. That is not the case in your example. If the declaration is in strict code, then no 'with' or 'eval' can intervene between the declaration and its use (except for the potential issue with indirect eval and the toplevel that I mentioned, but which should be solvable).

I prefer algorithmic specifications over prose but I think it would add too

much complexity to the spec.

I agree, but this one is rather straightforward to spec, clearly delimited, and not dependent on fancy analysis.

to algorithmically specify when an early error must be reported on assignments that may target const bindings. In this case I think the best we can do will be something like: If it can be statically proven prior to execution that an assignment will always result in an ReferenceError the error may be reported as an early error.

Yes, I completely agree that introducing implementation-dependent behaviour like that doesn't sound like a good idea.

# Brendan Eich (14 years ago)

Allen Wirfs-Brock wrote:

On Mar 21, 2012, at 10:11 AM, Brendan Eich wrote:

Andreas Rossberg wrote:

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour.

Agreed, absolutely. In all-strict code we know all the bindings, we see all assignments.

Yes, but just because a function is strict doesn't mean that everything is strict:

{const c=0; with (someObj) { (function() {"use strict"}; c=2})(); } }

Yup, hence my "all-strict" short-hand for Andreas's more carefully worded "iff the declaration is in strict code -- which generally implies that the use site and any scope in-between is strict code ...."

# Allen Wirfs-Brock (14 years ago)

On Mar 22, 2012, at 9:10 AM, Brendan Eich wrote:

Allen Wirfs-Brock wrote:

On Mar 21, 2012, at 10:11 AM, Brendan Eich wrote:

Andreas Rossberg wrote:

Right, but my comment was only about checking of assignments to const variables. Those are always an error, regardless of TDZ behaviour. Agreed, absolutely. In all-strict code we know all the bindings, we see all assignments.

Yes, but just because a function is strict doesn't mean that everything is strict:

{const c=0; with (someObj) { (function() {"use strict"}; c=2})(); } }

Yup, hence my "all-strict" short-hand for Andreas's more carefully worded "iff the declaration is in strict code -- which generally implies that the use site and any scope in-between is strict code ...."

I agree, assignment to such declarations should always be early errors. It may still end of being most practical to specify this in propose