Strict mode recap
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Waldemar Horwat Sent: 31. mars 2008 18:03 To: es4-discuss at mozilla.org Subject: Strict mode recap
Here are the items mentioned for inclusion in strict mode:
- Don't turn a null 'this' value into the global object (if non-strict mode in ES4 doesn't already do this)
- Throw on writes to read-only properties
- Throw on deletes of dontdelete properties
- delete o.x when x is not in o but in the proto will throw
Actually delete o.x when x is not an own property on o, regardless of whether x in o.
- Reference before definition causes static errors (in some contexts?)
Subject to further refinement and the belief in the WG that the necessary analysis is affordable on smaller systems. To my knowledge this is one of two items on the list requiring interesting compile-time analysis, and though it is desirable it may be too much, given that the run-time semantics are clear enough. We had previously placed all interesting compile-time analyses in the verifier, which was considered optional.
- Function arity checking at run time (ES4 strict only)
- Disable global variable auto-creation when referencing one from, for example, within a function
- Disallow duplicate formal parameters
- Disallow duplicate names in object initializers
- Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
This is an interesting one, since disallowing it would mean that the ES3.1 and ES4 specs would have to re-allow it so that they could explicitly disallow it :)
- (Maybe) Disallow use of arguments object (ES4 strict only)
- (Maybe) Disallow useless expressions
Again, compile-time analysis, though lighter weight. Hard to get right and useful at the same time, consider that (x + "") may have effects if x is not a string (its toString is called).
- Prohibit 'with' and locally scoped 'eval'. Globally scoped 'eval', 'new Function', etc. would still be allowed.
Having thought more about this, we may get away with less draconian measures for lexically scoped eval -- it's enough to simply decree that eval may not add bindings to the caller's binding object in strict mode (a simple run-time check, effectively the same as disabling global variable auto-creation).
The observation is that reference forms like expr::[expr] require the same functionality as the constrained, lexically scoped eval would require anyway, so the benefit of outlawing eval except for matters of taste is doubtful both in terms of implementation cost and simplified semantics.
Thanks for posting the list, I will put together a coherent proposal we can discuss.
For context, a brief summary of the consensus agreement we reached at the ES committee meeting:
The EcmaScript Committee will produce two official standards documents:
- An ES3.1 standard, to supercede the ES3 standard
- An ES4 standard Currently, the ES3 spec as written differs from the de-facto ES3 as implemented by web browsers and as employed by web page authors.
- Both ES3.1 and ES4 seek to be a compatible superset of what Brendan calls ES3+Reality.
- Both groups will work together to help ensure that ES3.1 be a compatible subset of ES4, i.e., that ES4 be a compatible superset of ES3.1.
- The ES3.1 spec will be written by modifying the ES3 spec text, which it will supercede.
- The ES4 spec will proceed along its current lines, and will be available as a distinct document.
- The ES4 reference implementation (RI), though non-normative, will serve as the initial bridge helping ensure that these two languages have the intended compatibility relationships.
- The RI will also serve as the ES3.1 reference implementation -- by testing the subset of its behavior corresponding to the ES3.1 semantics.
- When this subset of the RI's behavior differs from the intended semantics of ES3.1, the ES3.1 WG will alert the ES4 WG, so that we can work together to repair the subset relationship.
Late Friday, after several people had left, those remaining discussed an interesting refinement. Before I summarize the refinement, I wish to stress that these are not yet points discussed with the committee as a whole.
Until the Friday refinement, both ES3.1 and ES4 were going to condition certain restrictions on opting in to the language version. Both would condition further restrictions on opting into "strict mode". The relationship between ES3.1 strict mode and ES4 strict mode was unclear.
Late Friday, the ES4 WG presented to the joint group their latest thinking on ES4 strict mode, essentially as summarized by Waldemar. These restrictions, and the removal of the static type checking restriction from ES4 strict mode, makes it essentially compatible with the restrictions needed by ES3.1. With the exception of two issues, it seems we could now have the following set of relationships:
a) ES3.1 strict <= ES3.1 b) ES4 strict <= ES4 c) ES3.1 <= ES4 d) ES3.1 strict <= ES4 strict e) 3/4 ES3+R <= ES3.1 f) Caja <= ES3.1 strict
where "<=" means "subset". The precise definition of "subset" was not explicitly discussed during the committee meetings, but the intuition seems compatible with the notion of "fail-stop subset" as defined in the Caja spec.
#e above means that ES3.1 standard mode would be approximately a codification of ES3+Reality. In other words, for a browser to be compatible with the de-facto use of JavaScript on the web, it should be able to follow the ES3.1 std mode spec faithfully. In fact, the ES3.1 effort started mostly in order to perform this codification. ES3+R is a bit misshappen, especially regarding features like lexically nested functions. The "<=" in #e indicates that ES3.1 will be a bit larger than ES3+R, in order to be a simpler and better defined language that includes ES3+R. The "3/4" in #e indicates that presence in 3/4 of the major browsers (IE, FF, Opera, Safari) was adequate to be a candidate for inclusion.
This 3/4 notion needs one further bit of refinement. The hard constraint is not that ES3.1 have an upwards compatible semantics from 3/4 ES3+R, but that it have an upwards compatible syntax. An ES3.1 program should successfully parse on existing most of the current ES3 engines, so that it can inquire about its environment -- by feature testing and comparing MAX_ECMASCRIPT_VERSION -- and then conditionally execute code according to what it finds. This is the hardest constraint separating the ES3* line from the ES4* line.
With this background, let's revisit Waldemar's list.
On Mon, Mar 31, 2008 at 5:02 PM, Waldemar Horwat <waldemar at google.com> wrote:
Here are the items mentioned for inclusion in strict mode:
- Don't turn a null 'this' value into the global object (if non-strict mode in ES4 doesn't already do this)
Much as I hate this particular privilege escalation hazard, in order to be >= reality, I think this must be sane only in strict mode. ES3.1
and ES4 should be in complete agreement here.
- Throw on writes to read-only properties
- Throw on deletes of dontdelete properties
- delete o.x when x is not in o but in the proto will throw
- Reference before definition causes static errors (in some contexts?)
- Function arity checking at run time (ES4 strict only)
I am at a loss on this one. ES3.1 has no alternative way to implement variable arity and optional arguments, even in strict mode. OTOH, I quite understand why ES4 strict wants to enforce this. But these respective stances break the subsetting we desire. It seems we have our first hard case for working together to repair the subset relationship. Ideas?
- Disable global variable auto-creation when referencing one from, for example, within a function
- Disallow duplicate formal parameters
- Disallow duplicate names in object initializers
- Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
- (Maybe) Disallow use of arguments object (ES4 strict only)
After the official meeting broke up completely, several of us (Lars, Dave, Jeff, ??) continued talking in the Mozilla kitchen. A proposal for rescuing the arguments object:
In strict mode, the arguments object would be a read-only array. In a function mentioning "arguments", all it parameter variables would be read-only, as if declared with "const".
With these restrictions, the strict behavior would both be a fail-stop subset of the standard behavior, and be well behaved.
On Mon, Mar 31, 2008 at 7:09 PM, Lars Hansen <lhansen at adobe.com> wrote:
[mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Waldemar Horwat
- delete o.x when x is not in o but in the proto will throw
Actually delete o.x when x is not an own property on o, regardless of whether x in o.
I didn't understand this.
- Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
This is an interesting one, since disallowing it would mean that the ES3.1 and ES4 specs would have to re-allow it so that they could explicitly disallow it :)
Yes. It's also an interesting test of how strong our stomachs are in codifying reality. Any web browser that doesn't provide these will break (or be broken by) the web. However, it has never been specified and should never have been implemented or used. I do think that standards mode should include it and strict mode should ban it. Otherwise, de-facto JavaScript continues to differ too greatly from what's documented.
- Prohibit 'with' and locally scoped 'eval'. Globally scoped 'eval', 'new Function', etc. would still be allowed.
Having thought more about this, we may get away with less draconian measures for lexically scoped eval -- it's enough to simply decree that eval may not add bindings to the caller's binding object in strict mode (a simple run-time check, effectively the same as disabling global variable auto-creation).
The observation is that reference forms like expr::[expr] require the same functionality as the constrained, lexically scoped eval would require anyway, so the benefit of outlawing eval except for matters of taste is doubtful both in terms of implementation cost and simplified semantics.
It's interesting. I have become so allergic to locally-scoped eval that I started writing down my killer arguments against it. They all dissolved in the writing. It still makes me deeply uncomfortable, but your milder restriction actually deals with all the killer arguments I was able to generate so far. Hmmm...
Thanks for posting the list, I will put together a coherent proposal we can discuss.
Looking forward to it!
- Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
This is an interesting one, since disallowing it would mean that the ES3.1 and ES4 specs would have to re-allow it so that they could explicitly disallow it :)
Yes. It's also an interesting test of how strong our stomachs are in codifying reality. Any web browser that doesn't provide these will break (or be broken by) the web. However, it has never been specified and should never have been implemented or used. I do think that standards mode should include it and strict mode should ban it. Otherwise, de-facto JavaScript continues to differ too greatly from what's documented.
One of the important goals of ES3+R/ES3.1 is to be allow people to create new JS interpreter from the spec and have it work with the web. Therefore we do need to specify FunctionObject.arguments even though we all dislike it.
Mark Miller wrote:
- Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
This is an interesting one, since disallowing it would mean that the ES3.1 and ES4 specs would have to re-allow it so that they could explicitly disallow it :)
Yes. It's also an interesting test of how strong our stomachs are in codifying reality. Any web browser that doesn't provide these will break (or be broken by) the web. However, it has never been specified and should never have been implemented or used. I do think that standards mode should include it and strict mode should ban it. Otherwise, de-facto JavaScript continues to differ too greatly from what's documented.
I think this would be going too far. I would not want to normatively introduce this little abomination into the spec even if it's de facto used by legacy scripts in browsers -- it was never in the spec and there are other uses of JavaScript not in browsers that shouldn't have to suffer from it. The same goes for a few of the regexp features/bugs that aren't in ES3.
Waldemar
-----Original Message----- From: Mark Miller [mailto:erights at gmail.com] Sent: 31. mars 2008 21:36 To: Lars Hansen Cc: Waldemar Horwat; es4-discuss at mozilla.org Subject: Re: Strict mode recap
- Disable FunctionObject.arguments (not actually in ES3 but
woefully used in practice)This is an interesting one, since disallowing it would mean that the ES3.1 and ES4 specs would have to re-allow it so that they could
explicitly disallow it :)
Yes. It's also an interesting test of how strong our stomachs are in codifying reality. Any web browser that doesn't provide these will break (or be broken by) the web. However, it has never been specified and should never have been implemented or used.
It was actually written up in great normative detail in ES1 (section 10.1.6, 15.3.5.3 are the more important sections), though with some hedging but ineffectual language about "bad style" and "compatibility with old code".
Strangely, built-in functions were exempt from the mechanism (ch 15 intro); I don't know if that's a pragmatic issue (implementation reasons) or a security issue.
The mechanism is not described in ES2, which I thought was a bit strange in that ES2 was supposed to be simply(?) the bugfix version of ES1 that corresponded with the ISO standard.
On Apr 1, 2008, at 12:22 PM, Lars Hansen wrote:
Strangely, built-in functions were exempt from the mechanism (ch 15 intro); I don't know if that's a pragmatic issue (implementation reasons) or a security issue.
I don't recall, but it doesn't matter for natives, AFAICT.
The mechanism is not described in ES2, which I thought was a bit
strange in that ES2 was supposed to be simply(?) the bugfix version of ES1
that corresponded with the ISO standard.
ES2 added do-while and a few other things, IIRC. It was not just the
ISO version of ES1.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Lars Hansen Sent: 31. mars 2008 20:09 To: Waldemar Horwat; es4-discuss at mozilla.org Subject: RE: Strict mode recap
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Waldemar Horwat Sent: 31. mars 2008 18:03 To: es4-discuss at mozilla.org Subject: Strict mode recap
- delete o.x when x is not in o but in the proto will throw
Actually delete o.x when x is not an own property on o, regardless of whether x in o.
And since destructuring is just sugar for property references, this implies that destructuring that attempts to access fields not present will fail too, which is another thing that came up in a (possibly private) discussion around strict mode.
On 3/31/08, Lars Hansen <lhansen at adobe.com> wrote:
Having thought more about this, we may get away with less draconian measures for lexically scoped eval -- it's enough to simply decree that eval may not add bindings to the caller's binding object in strict mode (a simple run-time check, effectively the same as disabling global variable auto-creation).
The observation is that reference forms like expr::[expr] require the same functionality as the constrained, lexically scoped eval would require anyway, so the benefit of outlawing eval except for matters of taste is doubtful both in terms of implementation cost and simplified semantics.
Lars, does this mean that expr::[expr] can't introduce lexical bindings? Or: in strict mode, it can't, but in standard it can?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 2. april 2008 17:51 To: Lars Hansen Cc: Waldemar Horwat; es4-discuss at mozilla.org Subject: Re: Strict mode recap
On 3/31/08, Lars Hansen <lhansen at adobe.com> wrote:
Having thought more about this, we may get away with less draconian measures for lexically scoped eval -- it's enough to simply decree that eval may not add bindings to the caller's binding object in strict mode (a simple run-time check, effectively the same as disabling global variable auto-creation).
The observation is that reference forms like expr::[expr] require
the same functionality as the constrained, lexically scoped eval
would require anyway, so the benefit of outlawing eval except for
matters of taste is doubtful both in terms of implementation cost
and
simplified semantics.
Lars, does this mean that expr::[expr] can't introduce lexical bindings? Or: in strict mode, it can't, but in standard it can?
It can't introduce bindings; it's just a name.
On 4/2/08, Lars Hansen <lhansen at adobe.com> wrote:
Lars, does this mean that expr::[expr] can't introduce lexical bindings? Or: in strict mode, it can't, but in standard it can?
It can't introduce bindings; it's just a name.
I meant something like:
var foo::[bar] = baz;
My objection to expr::[expr] in earlier messages was based on the assumption that these computed names could be used on the left-hand side of an assignment expression -- which, I'm pretty sure, is syntactically valid.
So, for example:
var foo = "hello"; null::["foo"] = "goodbye"; print(foo); // prints "goodbye"
But I guess there are two cases: one where a new binding would be introduced and another where the expression would evaluate to an already bound name (as in the previous example).
So... are either of those cases legal?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 2. april 2008 19:06 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: Strict mode recap
On 4/2/08, Lars Hansen <lhansen at adobe.com> wrote:
Lars, does this mean that expr::[expr] can't introduce
lexical bindings? Or: in strict mode, it can't, but in > standard it can?It can't introduce bindings; it's just a name.
I meant something like:
var foo::[bar] = baz;
My objection to expr::[expr] in earlier messages was based on the assumption that these computed names could be used on the left-hand side of an assignment expression -- which, I'm pretty sure, is syntactically valid.
But that by itself can't introduce bindings (except global ones).
So, for example:
var foo = "hello"; null::["foo"] = "goodbye"; print(foo); // prints "goodbye"
But I guess there are two cases: one where a new binding would be introduced and another where the expression would evaluate to an already bound name (as in the previous example).
So... are either of those cases legal?
If you want to introduce a new binding then you have to do eg
ns var x = E
to introduce ns::x, and ns has to reference a namespace definition, so it's not variable. Nor is the x, obviously. But in that case:
var v1 = ns var v2 = "x" v1::[v2] = 20
updates ns::x, AFAIK. Nothing you can't do with lexically scoped eval.
On 4/2/08, Lars Hansen <lhansen at adobe.com> wrote:
I meant something like:
var foo::[bar] = baz;
My objection to expr::[expr] in earlier messages was based on the assumption that these computed names could be used on the left-hand side of an assignment expression -- which, I'm pretty sure, is syntactically valid.
But that by itself can't introduce bindings (except global ones).
I didn't know that, but I'm happy to hear it. I figured, from the syntactic form alone, that:
var foo::[bar] = ...
... would introduce a function-local binding (if the name wasn't already bound) -- since that's what var normally does.
If you want to introduce a new binding then you have to do eg
ns var x = E
to introduce ns::x, and ns has to reference a namespace definition, so it's not variable. Nor is the x, obviously.
Good. But then why allow:
var expr::[expr] = ...
... at all? (I'm specifically referring to the fact that 'var' appears before the name.) This has the syntactic form of a definition, but it can't actually be one, according to what you've written. It could only be an assignment. (Okay, I guess it could introduce a property on the global object, but that's already a special case and doesn't require definition syntax.)
But in that case:
var v1 = ns var v2 = "x" v1::[v2] = 20
updates ns::x, AFAIK. Nothing you can't do with lexically scoped eval.
That, by itself, isn't exactly an selling point; no one likes lexically scoped eval.
I find computed names less objectionable now that I know local bindings can't be introduced by them. (I thought that shadowing could occur, which wouldn't be detectable until runtime. Of course, that is true of 'with' -- but, again, hardly a selling point.) Can't say I'd be sorry if they were removed from the language, though.
Is this already in AS3? If so, is it often used?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 2. april 2008 20:51 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: Strict mode recap
On 4/2/08, Lars Hansen <lhansen at adobe.com> wrote:
I meant something like:
var foo::[bar] = baz;
My objection to expr::[expr] in earlier messages was based on > the assumption that these computed names could be used on the > left-hand side of an assignment expression -- which, I'm > pretty sure, is syntactically valid.
But that by itself can't introduce bindings (except global ones).
I didn't know that, but I'm happy to hear it. I figured, from the syntactic form alone, that:
var foo::[bar] = ...
... would introduce a function-local binding (if the name wasn't already bound) -- since that's what var normally does.
That form is not allowed, to my knowledge. If the grammar allows it then this could be (a) a bug or (b) missing semantic constraints.
If you want to introduce a new binding then you have to do eg
ns var x = E
to introduce ns::x, and ns has to reference a namespace
definition, so it's not variable. Nor is the x, obviously.Good. But then why allow:
var expr::[expr] = ...
... at all?
It's not.
(I'm specifically referring to the fact that 'var' appears before the name.)
I understand that.
This has the syntactic form of a definition, but it can't actually be one, according to what you've written. It could only be an assignment. (Okay, I guess it could introduce a property on the global object, but that's already a special case and doesn't require definition syntax.)
It should be illegal.
But in that case:
var v1 = ns var v2 = "x" v1::[v2] = 20
updates ns::x, AFAIK. Nothing you can't do with lexically scoped eval.
That, by itself, isn't exactly an selling point; no one likes lexically scoped eval.
I would say that "almost no one likes lexically scoped eval, almost all of the time". :) And the main reasons to dislike it are that it can introduce bindings and that it is invisible (if implemented like in Firefox, which is not required by ES3).
I find computed names less objectionable now that I know local bindings can't be introduced by them. (I thought that shadowing could occur, which wouldn't be detectable until runtime. Of course, that is true of 'with' -- but, again, hardly a selling point.) Can't say I'd be sorry if they were removed from the language, though.
Is this already in AS3? If so, is it often used?
The syntax comes from E4X, which is incorporated into AS3 and Spidermonkey. Try this in Firefox:
<script type="text/javascript;e4x=1"> var x = <ns:p xmlns:ns="www.opera.com">ns:qHi
there</ns:q></ns:p> var ns = new Namespace("www.opera.com"); var em = "q" document.writeln(x.ns::[em]); </script>
On Wed, Apr 2, 2008 at 11:15 PM, Lars Hansen <lhansen at adobe.com> wrote:
Is this already in AS3? If so, is it often used?
The syntax comes from E4X, which is incorporated into AS3 and Spidermonkey. Try this in Firefox:
<script type="text/javascript;e4x=1"> var x = <ns:p xmlns:ns="www.opera.com">ns:qHi there</ns:q></ns:p> var ns = new Namespace("www.opera.com"); var em = "q" document.writeln(x.ns::[em]); </script>
I'm aware of E4X. I meant: is it possible to refer to lexical bindings in this manner in AS3?
I guess I don't see what use case is satisfied by this feature that isn't already met by the existence of Map. After all, the effect of this feature is to turn lexical environments into (quasi) first class maps, with the peculiar restriction that new name/value pairs cannot be added, though existing ones can be mutated. If a user actually needs that functionality, it wouldn't be difficult to subclass Map to get it.
And, yes, I know it's already possible to do this with lexically scoped eval. But why do you want to add another way to do it?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 3. april 2008 10:10 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: Strict mode recap
On Wed, Apr 2, 2008 at 11:15 PM, Lars Hansen <lhansen at adobe.com> wrote:
Is this already in AS3? If so, is it often used?
The syntax comes from E4X, which is incorporated into AS3 and
Spidermonkey. Try this in Firefox:<script type="text/javascript;e4x=1"> var x = <ns:p xmlns:ns="www.opera.com">ns:qHi there</ns:q></ns:p> var ns = new Namespace("www.opera.com"); var em = "q" document.writeln(x.ns::[em]); </script>
I'm aware of E4X. I meant: is it possible to refer to lexical bindings in this manner in AS3?
Yes. AS3 has an annoying restriction on where user-defined namespaces may be used, but the following example captures it without using E4X:
package X { namespace myns;
class C {
myns var v = 37;
}
var x = myns;
var y = "v";
print((new C).x::[y]);
}
This does print '37' if compiled with ASC and run through the Tamarin VM.
I guess I don't see what use case is satisfied by this feature that isn't already met by the existence of Map.
After all, the effect of this feature is to turn lexical environments into (quasi) first class maps, with the peculiar restriction that new name/value pairs cannot be added, though existing ones can be mutated. If a user actually needs that functionality, it wouldn't be difficult to subclass Map to get it.
And, yes, I know it's already possible to do this with lexically scoped eval. But why do you want to add another way to do it?
Let me turn it around.
The syntax ns::v is in the language, for constant identifier v. If E4X is implemented in an implementation (as it will be in ActionScript and presumably in Firefox, at least), so is ns::[expr]. How would a restriction to require ns to be a compile-time constant (in either form) and not a run-time value, or a restriction to disallow the latter form in ES4 but not in E4X, benefit ES4?
On 4/3/08, Lars Hansen <lhansen at adobe.com> wrote:
package X { namespace myns;
class C { myns var v = 37; } var x = myns; var y = "v"; print((new C).x::[y]);
}
This does print '37' if compiled with ASC and run through the Tamarin VM.
This is like the E4X example in one crucial respect: in both cases, you're accessing a property of a first-class object. I'm not arguing against this use of computed names. I don't see any significant difference between the above and:
var obj = { x: "hello" }; print(obj["x"]);
My claim is simply that...
function foo() { var x = "hello"; }
... here, x is not a property of a first-class object. x's binding environment isn't a datum. But:
function foo(name) { ... return null::[name]; }
... treats the environment as if it were a datum. I know that in the ES3 spec all bindings are referred to as properties of objects, but activation objects are only notional entities. The current ES4 proposal seems to raise their status by giving programmers a simple mechanism to (practically) reify them.
I guess I don't see what use case is satisfied by this feature that isn't already met by the existence of Map.
After all, the effect of this feature is to turn lexical environments into (quasi) first class maps, with the peculiar restriction that new name/value pairs cannot be added, though existing ones can be mutated. If a user actually needs that functionality, it wouldn't be difficult to subclass Map to get it.
And, yes, I know it's already possible to do this with lexically scoped eval. But why do you want to add another way to do it?
Let me turn it around.
The syntax ns::v is in the language, for constant identifier v. If E4X is implemented in an implementation (as it will be in ActionScript and presumably in Firefox, at least), so is ns::[expr]. How would a restriction to require ns to be a compile-time constant (in either form) and not a run-time value, or a restriction to disallow the latter form in ES4 but not in E4X, benefit ES4?
From my perspective, it isn't an E4X vs. ES4 distinction; it's a "looking up a property of a first-class object" vs. "using a local variable" (or, if you prefer, "looking up a property of an activation object") distinction. It's not as if this is an uncommon distinction in programming languages, and the advantage of keeping the distinction is better static analysis, better performance, more tractable code.
On 04/04/2008, Jon Zeppieri <jaz at bu.edu> wrote:
This is like the E4X example in one crucial respect: in both cases, you're accessing a property of a first-class object. I'm not arguing against this use of computed names. I don't see any significant difference between the above and:
var obj = { x: "hello" }; print(obj["x"]);
My claim is simply that...
function foo() { var x = "hello"; }
... here, x is not a property of a first-class object. x's binding environment isn't a datum. But:
function foo(name) { ... return null::[name]; }
... treats the environment as if it were a datum. I know that in the ES3 spec all bindings are referred to as properties of objects, but activation objects are only notional entities. The current ES4 proposal seems to raise their status by giving programmers a simple mechanism to (practically) reify them.
Except you can't actually make the activation object a first class object. The null namespace (or any other namespace) is orthogonal to the activation object - you can now look up local variables dynamically instead of just statically, but the scope still cannot be leaked out of the function in any way.
The only notable change is that you now have a method of making dynamic lookup of the local variables in a scope instead of only static lookup. The method of doing so is considerably better than locally scoped eval because it's restricted to doing just that and nothing more.
On 4/3/08, liorean <liorean at gmail.com> wrote:
On 04/04/2008, Jon Zeppieri <jaz at bu.edu> wrote:
function foo(name) { ... return null::[name]; }
... treats the environment as if it were a datum. I know that in the ES3 spec all bindings are referred to as properties of objects, but activation objects are only notional entities. The current ES4 proposal seems to raise their status by giving programmers a simple mechanism to (practically) reify them.
Except you can't actually make the activation object a first class object. The null namespace (or any other namespace) is orthogonal to the activation object - you can now look up local variables dynamically instead of just statically, but the scope still cannot be leaked out of the function in any way.
Right, hence the 'practically.' There's no meaningful difference, though; the function simply acts as a proxy for the environment. I mean, what important difference is there (as far as the current discussion is concerned) between:
var map = new PeculiarMap(["foo", "bar", "baz"]);
... where PeculiarMap is a subclass of Map that maintains a constant set of keys, but allows those keys to be associated with new values, and:
var map = (function() { var foo, bar, baz;
return { get: function(name) { return null::[name]; },
set: function(name, value) { null::[name] = value; } }; })();
The fact that you can't pass around the activation object itself doesn't seem terribly significant. What would you be able to do if it were a real first-class object that you can't do here?
The only notable change is that you now have a method of making dynamic lookup of the local variables in a scope instead of only static lookup. The method of doing so is considerably better than locally scoped eval because it's restricted to doing just that and nothing more.
That's fine, but why would you want to do that, at all? My point is this: there's an obvious downside to this kind of lookup (inhibiting static analysis and all that), but there's no obvious upside to it.
I suppose the argument goes: if the choice is between calling eval to perform this kind of lookup or using ns::[expr], it's better to use ns::[expr]. I won't argue with that, but it's a false dilemma. You could just use an actual data structure.
eval has a real use: runtime code generation. (This does not require dynamically scoped eval, but that's beside the point here.) Dynamic lookup of activation object "properties," however, doesn't seem... useful. (I certainly could be wrong about this, but I can't think of a compelling use case. And, again, this does not apply to dynamic lookup of first-class object properties, which is obviously useful.) The point being: ES3 programmers who are using eval to perform dynamic lookup of activation object properties don't need a better mechanism to do the same thing, because what they're doing doesn't make sense to begin with.
On Thu, Apr 3, 2008 at 8:34 PM, Jon Zeppieri <jaz at bu.edu> wrote:
... treats the environment as if it were a datum. I know that in the ES3 spec all bindings are referred to as properties of objects, but activation objects are only notional entities. The current ES4 proposal seems to raise their status by giving programmers a simple mechanism to (practically) reify them.
If it's not too violent a change to the spec language, I would like to see the ES3.1 spec fix that. If we were modifying the spec to document only ES3.1 strict mode, that would probably be easy. But the new spec must account for both ES3.1 strict mode and ES3.1 loose mode.
A separate question: What do people think of "loose" for the opposite of "strict"?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 3. april 2008 21:35 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: Strict mode recap
Let me turn it around.
The syntax ns::v is in the language, for constant identifier v. If E4X is implemented in an implementation (as it will be in
ActionScript and presumably in Firefox, at least), so is
ns::[expr].
How would a restriction to require ns to be a compile-time constant (in either form) and not a run-time value, or a restriction to disallow the latter form in ES4 but not in E4X, benefit ES4?From my perspective, it isn't an E4X vs. ES4 distinction; it's a "looking up a property of a first-class object" vs. "using a local variable" (or, if you prefer, "looking up a property of an activation object") distinction. It's not as if this is an uncommon distinction in programming languages, and the advantage of keeping the distinction is better static analysis, better performance, more tractable code.
I don't get it.
Your comment about inhibiting static analysis isn't right; if ns::x is a name then either ns is known to be constant at compile time or not, and if it is, then nothing prevents early binding. ES3 systems perform analyses that are inhibited by eval and with, and statically detect whether eval and with are used in order to decide whether to perform the analyses.
Anyway, what's the distinction between "dynamic lookup of first-class object properties", as you write in a later message, and looking up a name in general, once you introduce namespaced bindings in packages and global objects (though not in local variables) and couple everything with 'with'? Are you saying I should be allowed to say (supposing namespace 'French' exists):
French::hello()
but not
var language = French language::hello()
? What purpose does it serve to have this restriction if static analysis is not inhibited in the former case, as you wrongly claim it is?
From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri
Let me turn it around. From my perspective, it isn't an E4X vs. ES4 distinction; it's a "looking up a property of a first-class object" vs. "using a local variable" (or, if you prefer, "looking up a property of an activation object") distinction. It's not as if this is an uncommon distinction in programming languages, and the advantage of keeping the distinction is better static analysis, better performance, more tractable code.
On 04/04/2008, Lars Hansen <lhansen at adobe.com> wrote:
Your comment about inhibiting static analysis isn't right; if ns::x is a name then either ns is known to be constant at compile time or not, and if it is, then nothing prevents early binding. ES3 systems perform analyses that are inhibited by eval and with, and statically detect whether eval and with are used in order to decide whether to perform the analyses.
I thought the argument was about the ns::[name] form... While the namespace may be known at compile time, the actual variable name is not - which means that the implementation actually has to be able to look local variable names up as strings, instead of just object members.
That is similar in effect to locally scoped eval. ES3 allows restricting eval such that this string look up only has to work if the compiler actually sees an "eval" in the fucntion body, though. I guess the same argument can be made for these dynamic namespace lookups too, however.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of liorean Sent: 4. april 2008 10:47 To: es4-discuss at mozilla.org Subject: Re: Strict mode recap
On 04/04/2008, Lars Hansen <lhansen at adobe.com> wrote:
Your comment about inhibiting static analysis isn't right; if ns::x
is a name then either ns is known to be constant at compile time or
not, and if it is, then nothing prevents early binding. ES3 systems
perform analyses that are inhibited by eval and with, and
statically
detect whether eval and with are used in order to decide whether to
perform the analyses.
I thought the argument was about the ns::[name] form... While the namespace may be known at compile time, the actual variable name is not - which means that the implementation actually has to be able to look local variable names up as strings, instead of just object members.
That is similar in effect to locally scoped eval. ES3 allows restricting eval such that this string look up only has to work if the compiler actually sees an "eval" in the fucntion body, though. I guess the same argument can be made for these dynamic namespace lookups too, however.
Sure, the situation with a variable namespace name and variable identifier string is the same, and it is always visible to the compiler.
(BTW I believe that locally bound names in ES4 cannot have explicit namespaces, but that these names are defined in the common public namespace (the one Jon calls 'null' though it will probably end up being called 'public'). The forthcoming spec on names and name lookup will clarify.)
On Fri, Apr 4, 2008 at 11:30 AM, Lars Hansen <lhansen at adobe.com> wrote:
I don't get it.
Your comment about inhibiting static analysis isn't right; if ns::x is a name then either ns is known to be constant at compile time or not, and if it is, then nothing prevents early binding. ES3 systems perform analyses that are inhibited by eval and with, and statically detect whether eval and with are used in order to decide whether to perform the analyses.
I think we understand different things by "inhibiting static analysis." I'm not claiming that the inclusion of the feature makes static analysis impossible -- only that the use of the feature can severely limit its effectiveness. E.g.:
ns::[expr] = ...
... kills assignment analysis for any bindings in scope, and
ns1::[expr1] = ns2::[expr2]
... kills escape analysis similarly. (I think... although I'll happily defer to you on this.)
Now, if you want to say that it's pay as you go -- that if you use the feature, you incur the expense, and otherwise you don't -- I agree. I just don't yet understand why anyone would want to do this with activation objects. What are you paying for?
Anyway, what's the distinction between "dynamic lookup of first-class object properties", as you write in a later message, and looking up a name in general, once you introduce namespaced bindings in packages and global objects (though not in local variables) and couple everything with 'with'?
I'm not sure I understand this. I can't answer your question about namespaced bindings in packages, since I don't know what ES4 packages are. I have some vague idea that they are sugar for namespaces, but that may be wrong/obsolete. As for global objects, how does the inclusion of namespaces affect lookup? Unlike activation objects, globals aren't notional.
Oh, okay -- I think I follow you. So, in any given scope, ns::[expr] may refer to a local activation object binding or it may refer to a global one. (Forget 'with' for the moment.) I've been arguing that this kind of name shouldn't be allowed for local lookups, but it should be allowed for global ones. The problem is that you don't know which it is until the lookup is actually performed.
Well, I'd be happy making the criterion purely syntactic: assuming we aren't in a 'with' statement, ns::[expr] always refers to a global binding (not including ns::[expr] to the right of a property operator, of course.) In a 'with' statement, the with-ed object has to be searched first. 'with' certainly complicates lookup, but as you've pointed out before, its use is visible.
By the way, can a 'use namespace' pragma refer to a namespace via a variable?
Are you saying I should be allowed to say (supposing namespace 'French' exists):
French::hello()
but not
var language = French language::hello()
? What purpose does it serve to have this restriction if static analysis is not inhibited in the former case, as you wrongly claim it is?
Are you claiming that it's possible to perform early binding of language::hello? (Assuming the example isn't actually as simple as what you've written, I mean.)
-----Original Message----- From: Jon Zeppieri [mailto:zeppieri at gmail.com] Sent: 4. april 2008 11:02 To: Lars Hansen Cc: liorean; es4-discuss at mozilla.org Subject: Re: Strict mode recap
On Fri, Apr 4, 2008 at 12:57 PM, Lars Hansen <lhansen at adobe.com> wrote:
(BTW I believe that locally bound names in ES4 cannot have explicit
namespaces, but that these names are defined in the common public
namespace (the one Jon calls 'null' though it will probably end up being called 'public'). The forthcoming spec on names and name
lookup will clarify.)Wait... so what have we been arguing about?
I don't know what you have been arguing about :) I have been arguing that allowing a general reference form ns::[expr] where both ns and expr can be arbitrary expressions does not inhibit optimizations in a significant way, nor does a form ns::id where ns is an arbitrary expression, and furthermore that these forms have utility.
Classes can have namespaced properties and globals can too. These objects are in scope in a method (say), so in order to reference a property in them from the method, at a minimum the form ns::id must be available to the program (for constant ns and id). But since ns can be 'public' such a name can (as far as I understand it know) also match a local binding (parameter, let, var -- whatever), so this syntax is not at present restricted to finding properties in true (non activation record) objects. I hope the forthcoming spec on names will clarify this, so I suggest we hold off arguing about this particular point for the moment.
When ns and id are indeed constant then all sorts of early binding optimizations can be performed. If ns is not constant, or if id is actually some [expr], then the lookup is still by the same rules as for early binding, clearly, but it will have to be performed at run-time.
You can argue, and I think you are arguing, that there is no utility in allowing ns to be variable and allowing id to be replaced by [expr]. I disagree, based on my experience analyzing uses of eval in scripts written for the web, which to an astonishing degree evaluate simple variable names or object.property phrases.
On Fri, Apr 4, 2008 at 1:25 PM, Lars Hansen <lhansen at adobe.com> wrote:
I hope the forthcoming spec on names will clarify this, so I suggest we hold off arguing about this particular point for the moment.
Happily.
You can argue, and I think you are arguing, that there is no utility in allowing ns to be variable and allowing id to be replaced by [expr].
Specifically for local variables, yes.
I disagree, based on my experience analyzing uses of eval in scripts written for the web, which to an astonishing degree evaluate simple variable names or object.property phrases.
I've seen this, too, but rather than proving the utility of this feature, I tend to consider it as evidence of a silent epidemic of brain damage among ES programmers...
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 4. april 2008 11:38 To: Lars Hansen Cc: liorean; es4-discuss at mozilla.org Subject: Re: Strict mode recap
I disagree, based on my experience analyzing uses of eval in
scripts
written for the web, which to an astonishing degree evaluate simple
variable names or object.property phrases.
I've seen this, too, but rather than proving the utility of this feature, I tend to consider it as evidence of a silent epidemic of brain damage among ES programmers...
That could be true. But it could also be the case that people find that it is easier to say
var x = ... var y = ... var n = "x" // but could be y
... // n may change here by assigning another string to it ... eval(n)
than
var x = ... var y = ... var n = function () { return x } // but could be y
... // n may change here by assigning another function to it ... n()
(and the latter does not handle shadowing, for better or worse).
On Fri, Apr 4, 2008 at 1:45 PM, Lars Hansen <lhansen at adobe.com> wrote:
That could be true. But it could also be the case that people find that it is easier to say
var x = ... var y = ... var n = "x" // but could be y
... // n may change here by assigning another string to it ... eval(n)
than
var x = ... var y = ... var n = function () { return x } // but could be y
... // n may change here by assigning another function to it ... n()
Like I wrote in previous messages, what these programmers need is an actual data structure:
var m = { x: ..., y: ... }; var n = "x";
// n may change, &c. ... m[n]
(and the latter does not handle shadowing, for better or worse).
Heh. Someone should revive Mark Miller.
On Fri, Apr 4, 2008 at 2:42 PM, Mark Miller <erights at gmail.com> wrote:
On Fri, Apr 4, 2008 at 11:24 AM, Jon Zeppieri <jaz at bu.edu> wrote:
(and the latter does not handle shadowing, for better or worse).
Heh. Someone should revive Mark Miller.
[startles] What? I'm awake. Can someone summarize the issue in a self-contained manner?
It was just a joke -- I figured that Lars's example was exactly kind of use of lexically scoped eval that would trigger the allergic reaction you mentioned earlier in this thread.
On 4/4/08, Lars Hansen <lhansen at adobe.com> wrote:
Classes can have namespaced properties and globals can too. These objects are in scope in a method (say), so in order to reference a property in them from the method, at a minimum the form ns::id must be available to the program (for constant ns and id). But since ns can be 'public' such a name can (as far as I understand it know) also match a local binding (parameter, let, var -- whatever), so this syntax is not at present restricted to finding properties in true (non activation record) objects. I hope the forthcoming spec on names will clarify this, so I suggest we hold off arguing about this particular point for the moment.
On the compatibility namespace: "There is currently no way to explictly qualify a reference with noNS." If that remains the case, then all local variable references, other than those performed by lexically scoped eval and those obscured by a with statement, can be resolved ahead of time. I think that would be a good thing.
The spec authors, however, are concerned by the fact that "code inside of a WithStatement might want to refer to a local variable in an outer scope without the possibility of being shadowed by a property on the with object." I'm ambivalent about this. My initial reaction was to wonder why any code that could be affected by shadowing in this way would ever be in a with statement. Then I realized that most code in actual with statements probably could be affected by this, and the proposal was really meant as a partial bug fix for with statements.
So, here are my thoughts:
-
Taking advantage of the ability to avoid evil with-shadowing in this manner requires users to qualify the appropriate names explicitly. The time spent doing that would be better spent turning the with statement into a block with a let definition and explicit object property references. (Actually, let statements would be the best replacements for with statements. Too bad they were removed.) On the other hand, references inside a with statement are presumably (this is not based on any evidence) more likely to refer to with-object properties than not, so there may be significantly fewer local variable references to qualify.
-
The suggested fix depends upon the (unwritten) stricture that definitions cannot explicitly be qualified with noNS. Otherwise, given the example from the spec:
function f (o) { var count = 10 with (o) { for (var i = null::count; i > 0; --i) { ... } } }
var o = { null var count = 100, ... }; f(o);
... would make f do the wrong thing. This asymmetry (explicit reference but no explicit definition) is a minor inelegance. (Though maybe it's true of some other namespaces, as well?)
- Brendan's reformed with statement addresses the same problem without treating local binding environments as presumptive first class objects. No surprise, I prefer a specific fix to a specific problem, rather than adding what amounts to a restricted use of lexically scoped eval as a general language feature.
On 4/10/08, Jon Zeppieri <jaz at bu.edu> wrote:
var o = { null var count = 100, ... };
Sorry: two syntactic mistakes, here, one of which is interesting. The boring one is my use of '=' rather than ':'.
But after reading the object initializer draft, I see that, if the intent of the above were legal, the syntax would be:
var o = { var null::count: 100 };
Is it still the case that instance variables in classes are defined like:
public var count = ...
If so, why the difference (discounting the colon/equal part, that is)?
On Apr 10, 2008, at 8:30 PM, Jon Zeppieri wrote:
On 4/10/08, Jon Zeppieri <jaz at bu.edu> wrote:
var o = { null var count = 100, ... };
Sorry: two syntactic mistakes, here, one of which is interesting. The boring one is my use of '=' rather than ':'.
But after reading the object initializer draft, I see that, if the intent of the above were legal, the syntax would be:
var o = { var null::count: 100 };
Is it still the case that instance variables in classes are defined
like:public var count = ...
If so, why the difference (discounting the colon/equal part, that is)?
Part of the answer (enough for me) has to be that we want to support
var o = { iterator::get: ... }
without var in front of the property name. Not { iterator get: ... }
which is too spacey and similar to { get foo() ... } and other
special forms involving contexual keywords we don't want to preempt
from being namespace names.
Composing var in front with ns:: before identifier and you get the
unclassy { var ns::foo: ... }. Something has to "give" here -- either
ns var foo wins leaving us with ns foo (the var-free case of a
namespace-qualified property name) or it loses and we have what is
proposed.
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 10. april 2008 21:31 To: Lars Hansen Cc: liorean; es4-discuss at mozilla.org Subject: Re: Strict mode recap
On 4/10/08, Jon Zeppieri <jaz at bu.edu> wrote:
var o = { null var count = 100, ... };
Sorry: two syntactic mistakes, here, one of which is interesting. The boring one is my use of '=' rather than ':'.
But after reading the object initializer draft, I see that, if the intent of the above were legal, the syntax would be:
var o = { var null::count: 100 };
I don't think null is a valid namespace (but I'm still waiting for Jeff's spec on names to verify that) and it would in any case be redundant; when we talked about it being legal, the meaning was "the compatibility namespace", which is what you get by just leaving it off.
Apart from that there's no problem. "var" is used to indicate a mutable fixture property, but it is just a flag (as is "const"). The primary form is just { ns::id: val }, which is a straightforward evolution of the ES3 form { ns: val }.
On 4/11/08, Lars Hansen <lhansen at adobe.com> wrote:
On 4/10/08, Jon Zeppieri <jaz at bu.edu> wrote:
var o = { null var count = 100, ... };
Sorry: two syntactic mistakes, here, one of which is interesting. The boring one is my use of '=' rather than ':'.
But after reading the object initializer draft, I see that, if the intent of the above were legal, the syntax would be:
var o = { var null::count: 100 };
I don't think null is a valid namespace (but I'm still waiting for Jeff's spec on names to verify that) and it would in any case be redundant; when we talked about it being legal, the meaning was "the compatibility namespace", which is what you get by just leaving it off.
This part of my (first) email was about the compatibility namespace section of the names spec, which has a couple of 'fixme' paragraphs. The first argues for a way to qualify references explicitly with the compatibility namespace, and the second makes the case that while local lexical bindings should be in the compatibility namespace, unqualified object literal properties should be in the public namespace. The purpose of these, jointly, is to give people a reliable way to avoid unintended name shadowing in a with statement.
Apart from that there's no problem. "var" is used to indicate a mutable fixture property, but it is just a flag (as is "const"). The primary form is just { ns::id: val }, which is a straightforward evolution of the ES3 form { ns: val }.
Right, I get that, and Brendan's point was a good one. I was just thrown by the fact that I hadn't seen an example of
class A { var public::count = 10; }
rather than
class A { public var count = 10; }
But if the former is legal (and the grammar suggests that it is), then there's no inconsistency.
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 11. april 2008 05:20 To: Lars Hansen Cc: liorean; es4-discuss at mozilla.org Subject: Re: Strict mode recap
Apart from that there's no problem. "var" is used to indicate a mutable fixture property, but it is just a flag (as is "const"). The primary form is just { ns::id: val }, which is a
straightforward evolution of the ES3 form { ns: val }.Right, I get that, and Brendan's point was a good one. I was just thrown by the fact that I hadn't seen an example of
class A { var public::count = 10; }
rather than
class A { public var count = 10; }
But if the former is legal (and the grammar suggests that it is), then there's no inconsistency.
It is not legal, and if the grammar suggests that it is then the grammar is buggy.
On Fri, Apr 11, 2008 at 11:04 AM, Lars Hansen <lhansen at adobe.com> wrote:
Right, I get that, and Brendan's point was a good one. I was just thrown by the fact that I hadn't seen an example of
class A { var public::count = 10; }
rather than
class A { public var count = 10; }
But if the former is legal (and the grammar suggests that it is), then there's no inconsistency.
It is not legal, and if the grammar suggests that it is then the grammar is buggy.
Okay, so why is it a good thing to mandate a different syntax for defining an object property in an initializer, on one hand, and defining a property of a class instance, on the other? Don't get me wrong: I understand the utility of allowing the "public var count = ..." syntax, where 'var' in interposed between the two parts of the name. What I don't understand is why you wouldn't want a single, canonical syntax for expressing names in definitions. "public var count": could just be sugar for "var public::count". Or does that raise other problems...?
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 11. april 2008 09:26 To: Lars Hansen Cc: es4-discuss at mozilla.org; liorean Subject: Re: Strict mode recap
On Fri, Apr 11, 2008 at 11:04 AM, Lars Hansen <lhansen at adobe.com> wrote:
Right, I get that, and Brendan's point was a good one. I was > just thrown by the fact that I hadn't seen an example of > class A { var public::count = 10; }
rather than
class A { public var count = 10; }
But if the former is legal (and the grammar suggests that it > is), then there's no inconsistency.
It is not legal, and if the grammar suggests that it is then the grammar is buggy.
Okay, so why is it a good thing to mandate a different syntax for defining an object property in an initializer, on one hand, and defining a property of a class instance, on the other? Don't get me wrong: I understand the utility of allowing the "public var count = ..." syntax, where 'var' in interposed between the two parts of the name. What I don't understand is why you wouldn't want a single, canonical syntax for expressing names in definitions. "public var count": could just be sugar for "var public::count". Or does that raise other problems...?
As I wrote earlier, the canoncial cases are { id: 10 } and { ns::id: 10 }, where the property is dynamic. "var" and "const" are flags that make the properties into fixtures. Having to split ns::id apart to interpolate "var" does not appeal to me (at least).
I think you're getting hung up on the keyword. We could choose another keyword here to serve as the flag: { fixture ns::id: 10 } and there would not be a problem with that change particularly, but it also doesn't seem to serve any particularly good purpose to do so.
In other words
fixture { x: 10, y: 20 } // instead of var { fixture x: 10, y: 20 } // x is a fixture, not y
and
readonly { x: 10, y: 20 } // instead of const { readonly x: 10, y: 20 } // x is a readonly fixture, not y
and so on. Did we gain anything in clarity? Would we, if even better keywords were chosen?
On Fri, Apr 11, 2008 at 11:42 AM, Lars Hansen <lhansen at adobe.com> wrote:
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri
Sent: 11. april 2008 09:26 To: Lars Hansen
Cc: es4-discuss at mozilla.org; liorean Subject: Re: Strict mode recap
On Fri, Apr 11, 2008 at 11:04 AM, Lars Hansen <lhansen at adobe.com> wrote:
Right, I get that, and Brendan's point was a good one. I was > just thrown by the fact that I hadn't seen an example of > class A { var public::count = 10; }
rather than
class A { public var count = 10; }
But if the former is legal (and the grammar suggests that it > is), then there's no inconsistency.
It is not legal, and if the grammar suggests that it is then the grammar is buggy.
Okay, so why is it a good thing to mandate a different syntax for defining an object property in an initializer, on one hand, and defining a property of a class instance, on the other? Don't get me wrong: I understand the utility of allowing the "public var count = ..." syntax, where 'var' in interposed between the two parts of the name. What I don't understand is why you wouldn't want a single, canonical syntax for expressing names in definitions. "public var count": could just be sugar for "var public::count". Or does that raise other problems...?
As I wrote earlier, the canoncial cases are { id: 10 } and { ns::id: 10 }, where the property is dynamic. "var" and "const" are flags that make the properties into fixtures. Having to split ns::id apart to interpolate "var" does not appeal to me (at least).
I think you're getting hung up on the keyword. We could choose another keyword here to serve as the flag: { fixture ns::id: 10 } and there would not be a problem with that change particularly, but it also doesn't seem to serve any particularly good purpose to do so.
In other words
fixture { x: 10, y: 20 } // instead of var { fixture x: 10, y: 20 } // x is a fixture, not y
and
readonly { x: 10, y: 20 } // instead of const { readonly x: 10, y: 20 } // x is a readonly fixture, not y
and so on. Did we gain anything in clarity? Would we, if even better keywords were chosen?
--lars
No -- I agree with everything you wrote here. I like this syntax. The question is: why not apply it to classes, too?
By the way, I was wrong about the grammar allowing
var public::count = ...
in classes. It doesn't. But why not allow it there and consider it canonical? (Doesn't 'var' also indicate that the property is a fixture in a class definition, too? It's not part of the name, anyway.)
I'm not suggesting allowing
public var count: ...
in object initializers. I think Brendan's reply to me on that was decisive.
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 11. april 2008 09:50
... The question is: why not apply it to classes, too?
By the way, I was wrong about the grammar allowing
var public::count = ...
in classes. It doesn't. But why not allow it there and consider it canonical? (Doesn't 'var' also indicate that the property is a fixture in a class definition, too? It's not part of the name, anyway.)
One motivation is that programmers are likely to prefer the Java-like syntax where the namespace (in its role as access control) shows up early:
public var count = private var key =
I really think this is the "right" syntax for variables. The syntax
var private::key, private::foo, public::x, private::bar;
is certainly workable and unambiguous but
public var x; private var key, foo, bar;
works better for me, because the access control is visible and separated from the names, because each phrase is shorter, and because I'm guaranteed to separate my privates from my publics.
Classes are sort of funny since you can consider a property name in a class both as a property on the instance (o.id) but also just as a scoped variable, inside all the methods of the class. If you're really into Java the former case disappears completely because there will be getter/setter pairs for everything ;) so the "scoped variable" case is completely dominant. I'm not sure that's wrong, and I think a variable declaration syntax is more natural than an object property syntax.
(The syntax of field names in object initializers, on the other hand, is a consequence of how that syntax has evolved.)
Matters of taste? To an extent. But preserving "brain print" from other languages (Java, Python) is not unimportant, it's one of the principles that have been used in designing the language.
On Fri, Apr 11, 2008 at 12:17 PM, Lars Hansen <lhansen at adobe.com> wrote:
One motivation is that programmers are likely to prefer the Java-like syntax where the namespace (in its role as access control) shows up early:
public var count = private var key =
I really think this is the "right" syntax for variables. The syntax
var private::key, private::foo, public::x, private::bar;
is certainly workable and unambiguous but
public var x; private var key, foo, bar;
works better for me, because the access control is visible and separated from the names, because each phrase is shorter, and because I'm guaranteed to separate my privates from my publics.
Yep, I agree with this, too. I certainly wouldn't want to mandate
var public::x
in classes and wouldn't really want to write it myself. But there's some value in having a single, canonical syntactic form that will work everywhere. (If ES5 adds some kind of macro system, it would be more useful.)
Classes are sort of funny since you can consider a property name in a class both as a property on the instance (o.id) but also just as a scoped variable, inside all the methods of the class. If you're really into Java the former case disappears completely because there will be getter/setter pairs for everything ;) so the "scoped variable" case is completely dominant. I'm not sure that's wrong, and I think a variable declaration syntax is more natural than an object property syntax.
I agree with you on the natural syntax. I'm not too sure, however, that the property name/scoped variable thing is much more than a visual pun. (It would be different if classes were specified as closure creation sugar.) In
class A {
public var x;
public function getX() x
}
I tend to think of the reference to 'x' as an elliptical form of 'this.x'. And then there is
class A {
public var x;
public static function brokenGetX() x
}
... where the "scoped variable" analogy doesn't work.
On Fri, Apr 11, 2008 at 1:05 PM, Jon Zeppieri <jaz at bu.edu> wrote:
Yep, I agree with this, too. I certainly wouldn't want to mandate
var public::x
in classes and wouldn't really want to write it myself. But there's some value in having a single, canonical syntactic form that will work everywhere. (If ES5 adds some kind of macro system, it would be more useful.)
In case it isn't clear, I'm not asking "Why not drop the Java-like syntax?" but rather, "Why not consider the Java-like syntax as sugar on top of a canonical syntax that works in initializers and classes?"
-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 11. april 2008 11:10 To: Lars Hansen Cc: es4-discuss at mozilla.org; liorean Subject: Re: Strict mode recap
On Fri, Apr 11, 2008 at 1:05 PM, Jon Zeppieri <jaz at bu.edu> wrote:
Yep, I agree with this, too. I certainly wouldn't want to mandate
var public::x
in classes and wouldn't really want to write it myself.
But there's
some value in having a single, canonical syntactic form that will work
everywhere. (If ES5 adds some kind of macro system, it would be more useful.)In case it isn't clear, I'm not asking "Why not drop the Java-like syntax?" but rather, "Why not consider the Java-like syntax as sugar on top of a canonical syntax that works in initializers and classes?"
Because more syntax adds complication without adding value?
Anyhow there are some parsing problems. Consider that if C is a class with a statically defined namespace property:
class C { static namespace ns = ... }
then this is valid:
class D { C.ns var x = ... }
but if you try to translate it
class D { var C.ns::x = ... }
then you run up against the fact that that parses as C.(ns::x) normally but would have to parse as (C.ns)::x here to make sense. Yet one more rule.
It's not a showstopper, just an indication that it's probably time to leave well enough alone. (It is an indication that the syntax used in the object initializers is not fully general, though, since it only allows simple identifiers in the namespace position. Sigh.)
(If ES5 adds macros then we can always extend the syntax for variable definitions in ES5, we don't need to worry about that now. Future-proofing -- another principle that has seen some (ab)use in ES4 -- requires only that we don't close off possibilities, not that we make use of them.)
On Apr 11, 2008, at 10:22 AM, Lars Hansen wrote:
(It is an indication that the syntax used in the object initializers is not fully general, though, since it only allows simple identifiers in the namespace position. Sigh.)
I've argued that JS's literal property identifiers in object
initialisers, instead of mandatory quoted strings for literal names
and evaluated expressions for runtime naming, is a virtue, pace
Python. It certainly reduces the quote burden compared to JSON or
Python. It allows readers and compilers to make static judgments
about what names are bound in the object created for the initialiser.
Anyway, it's an old decision, hard to change now.
I'm mailing mainly to ask whether this restriction is something
considered harmful in ES4 with namespaces, or for any other reason. I
think Jon and I have agreed in the past on namespaces being constant,
but argument has evolved since then.
My reason for agreeing with Jon then was that readers, never mind
compilers, otherwise can have a hard time figuring out the meaning of
names. This is always hard with globals, less so with outer names in
closures, and no picnic with property initialisers if you add
computed namespaces to them.
I don't have a stronger reason than favoring comprehension and easing
implementation, though. The second is less important than the first,
but we consider efficiency too.
There might be a slight misunderstanding here. In my example, the name C.ns is constant, not a general expression; C needs to be a class, and ns needs to be a static namespace definition inside that class (suitably available).
In my (repentant) opinion the ns in any ns::id expression must reference a namespace binding that was not introduced into the scope by "with" (and I'm happy to outlaw all such expressions in the body of a "with", if that helps keep it simple).
I think you're trying to say something else too but I can't figure out what it is, something about the ns in ns::id being a literal in a stronger sense than what I just outlined?
On Apr 11, 2008, at 12:51 PM, Lars Hansen wrote:
There might be a slight misunderstanding here. In my example, the
name C.ns is constant, not a general expression; C needs to be a
class, and ns needs to be a static namespace definition inside that
class (suitably available).
Oh, ok. The general expression for namespace qualifier syntax,
whatever it will be, is what I was concerned about. If qualifiers in
object literals must be identifiers that resolve to namespace
definitions, then I'm not concerned about object initialisers being
harder to analyze (for people or programs). Although this may be too
restrictive, and I should share your concern about the loss of
computed namespace qualifier use-cases.
In my (repentant) opinion the ns in any ns::id expression must
reference a namespace binding that was not introduced into the
scope by "with" (and I'm happy to outlaw all such expressions in
the body of a "with", if that helps keep it simple).
Great.
I think you're trying to say something else too but I can't figure
out what it is, something about the ns in ns::id being a literal in
a stronger sense than what I just outlined?
Let me try to be clearer. In ES3,
obj = {prop: value}
is sugar for
$tmp = new Object // fixed in ES4 to not evaluate 'new Object' // but instead use memoized Object type $tmp.prop = value // evaluate value only, not prop the literal id obj = $tmp
All I am noting is that
obj = {ns::prop: value}
might want to involve no further evaluation of arbitrary expressions
than the ES3 case.
-----Original Message----- From: Brendan Eich [mailto:brendan at mozilla.org]
I think you're trying to say something else too but I can't figure
out what it is, something about the ns in ns::id being a literal in
a stronger sense than what I just outlined?
Let me try to be clearer. In ES3,
obj = {prop: value}
is sugar for
$tmp = new Object // fixed in ES4 to not evaluate 'new Object' // but instead use memoized Object type $tmp.prop = value // evaluate value only, not prop the literal id obj = $tmp
All I am noting is that
obj = {ns::prop: value}
might want to involve no further evaluation of arbitrary expressions
than the ES3 case.
The problem is that 'ns' does not have a fixed meaning, it can't, because namespaces can themselves be defined in namespaces.
Consider
namespace NS1; use namespace NS1 namespace NS2; use namespace NS2
NS1 namespace ns
function f() { return { ns::prop: value } }
NS2 namespace ns
Rewriting the body of the function as
let obj = new Object
obj.ns::prop = value
it is plain (to me) that I should get an error (there are two competing definitions for ns) when f is called and IMO that really indicates that there should be an error also in the original code.
What you are saying is that you're objecting to the computation (the lookup of ns) occuring even in the second case. I agree that that is far from desirable, for many reasons, but I have a hard time accepting that the introduction of NS2::ns should not have an effect on that code when f is called subsequently.
(I could make the same argument for types, obviously.)
I understand we can make special provisions for these cases to avoid this problem, eg, when f is processed the first time (whatever that means) ns is looked up and bound, and subsequent introductions of bindings for ns have no effect. This could be acceptable because namespaces are special kinds of bindings, not mutable variables. It's far from pretty. It requires definition before use, which is not too bad for namespaces; though I suppose ns could be bound when f is called the first time (sort of weird).
Here are the items mentioned for inclusion in strict mode:
Don't turn a null 'this' value into the global object (if non-strict mode in ES4 doesn't already do this)
Throw on writes to read-only properties
Throw on deletes of dontdelete properties
delete o.x when x is not in o but in the proto will throw
Reference before definition causes static errors (in some contexts?)
Function arity checking at run time (ES4 strict only)
Disable global variable auto-creation when referencing one from, for example, within a function
Disallow duplicate formal parameters
Disallow duplicate names in object initializers
Disable FunctionObject.arguments (not actually in ES3 but woefully used in practice)
(Maybe) Disallow use of arguments object (ES4 strict only)
(Maybe) Disallow useless expressions
Prohibit 'with' and locally scoped 'eval'. Globally scoped 'eval', 'new Function', etc. would still be allowed.
Waldemar