namespaces and expression-qualified names
On 3/21/08, Jon Zeppieri <jaz at bu.edu> wrote:
Are namespaces (and I'm not referring to the reflected objects here)...
Except I am referring to those, aren't I?
namespace ns1 ns1 is Namespace
true
So, at runtime, ns1 is the reflected object. That leaves that matter of expression-qualified names...
On Mar 20, 2008, at 11:31 PM, Jon Zeppieri wrote:
On 3/21/08, Jon Zeppieri <jaz at bu.edu> wrote:
Are namespaces (and I'm not referring to the reflected objects
here)...Except I am referring to those, aren't I?
What's a value? Types too have constant binding forms but runtime
value status. Namespaces and types may be useful at runtime, e.g. for
capability-like protection (you can read what you can name) and
reflection.
The choices so far for types have ruled out runtime type annotations,
in order to support the optional strict mode's static checker and
keep it efficient and fairly conventional. But we've talked about
alternatives such as contracts (a la PLT Scheme). This goes way back.
See, e.g.
proposals:contracts, doku.php? id=proposals:discussion_about_contracts
namespace ns1 ns1 is Namespace true
So, at runtime, ns1 is the reflected object. That leaves that matter of expression-qualified names...
I wrote in reply to Mark that ES4's namespaces did not derive from
E4X (ECMA-357). More the reverse, although of course XML namespaces
were primary. SpiderMonkey supports E4X and runtime namespace
qualifiers:
js> ns = new Namespace("hi")
hi js> o = {} [object Object] js> o.ns::p = 42
42 js> o.ns::p
42 js> id="p" // assume compiler can't propagate the constant "p"
p js> o.ns::[id]
42
This is all in Firefox 1.5-3.
I believe the intention among the core ES4 group over the last couple
of years has been to support runtime qualifiers in ES4, as well. JS
has computed property names since day 1: o[id] to access o.p. An
optional strict mode can't reason about these, but they aren't the
only loophole (eval, with, global non-fixtures). Why rule out runtime-
computed qualified names (with either part computed at runtime)?
Other core ES4 group members should chime in here.
On 3/21/08, Brendan Eich <brendan at mozilla.org> wrote:
I believe the intention among the core ES4 group over the last couple of years has been to support runtime qualifiers in ES4, as well. JS has computed property names since day 1: o[id] to access o.p. An optional strict mode can't reason about these, but they aren't the only loophole (eval, with, global non-fixtures). Why rule out runtime- computed qualified names (with either part computed at runtime)?
I'm talking about lexical bindings. I agree with you about object properties; computed names are already the norm. For lexical bindings, the situation (if I understand it correctly) is different. Because of the way eval is actually implemented in current engines, it's impractical (or maybe strictly impossible?) to resolve lexical lookups ahead of time. With ES4, though, only the operator form of eval will be able to modify lexical environments, and its use can be statically detected. Any environments that can be affected by the eval operator can be known at compile time, and they can use slow lookup. The same holds for with statements.
And, I think, the same would hold for runtime qualified names... but do you really want to add another way to defeat compile time lookup? I kind of got the impression that if there were no question of breaking the web, you would get rid of with (in its current, dynamic scoping form) and eval's ability to modify local environments.
On Mar 21, 2008, at 12:50 AM, Jon Zeppieri wrote:
I'm talking about lexical bindings.
Oh, that's different ;-).
In that case the namespace qualifier is analogous to a type
annotation -- in fact type names can be qualified. I agree that
lexical references of the form ns::id should require ns to be a
compile-time constant.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: 21. mars 2008 00:59 To: Jon Zeppieri Cc: es4-discuss Discuss Subject: Re: namespaces and expression-qualified names
On Mar 21, 2008, at 12:50 AM, Jon Zeppieri wrote:
I'm talking about lexical bindings.
Oh, that's different ;-).
In that case the namespace qualifier is analogous to a type annotation -- in fact type names can be qualified. I agree that lexical references of the form ns::id should require ns to be a compile-time constant.
I don't, so that's now on the agenda for the meeting next week.
On 3/21/08 12:50 AM, Jon Zeppieri wrote:
On 3/21/08, Brendan Eich <brendan at mozilla.org> wrote:
I believe the intention among the core ES4 group over the last couple of years has been to support runtime qualifiers in ES4, as well. JS has computed property names since day 1: o[id] to access o.p. An optional strict mode can't reason about these, but they aren't the only loophole (eval, with, global non-fixtures). Why rule out runtime- computed qualified names (with either part computed at runtime)?
This has been our intent, and what is implemented in AS3 (FWIW). It seems arbitrary and unnecessary to require qualifier expressions to be compile time constants. Correct me if I'm wrong, but isn't one of the common legitimate uses of 'eval' to computed lexical references? Computed names give users a chance to do so with better syntax and semantic checking.
As for lexical references with computed identifiers, ES4 also supports the following form:
expr1::[expr2]
where expr1 and expr2 are both computed at runtime.
I'm talking about lexical bindings. I agree with you about object properties; computed names are already the norm. For lexical bindings, the situation (if I understand it correctly) is different. Because of the way eval is actually implemented in current engines, it's impractical (or maybe strictly impossible?) to resolve lexical lookups ahead of time. With ES4, though, only the operator form of eval will be able to modify lexical environments, and its use can be statically detected. Any environments that can be affected by the eval operator can be known at compile time, and they can use slow lookup. The same holds for with statements.
And, I think, the same would hold for runtime qualified names... but do you really want to add another way to defeat compile time lookup?
Names that can be resolved at compile time won't (can't) change meaning at runtime. And names that are qualified by constant namespaces that appear in the (safe) contexts you describe above can be resolved at compile-time. So I don't understand your charge of runtime qualifiers "defeating compile time lookup". Please elaborate.
On Fri, Mar 21, 2008 at 11:12 AM, Jeff Dyer <jodyer at adobe.com> wrote:
Names that can be resolved at compile time won't (can't) change meaning at runtime. And names that are qualified by constant namespaces that appear in the (safe) contexts you describe above can be resolved at compile-time. So I don't understand your charge of runtime qualifiers "defeating compile time lookup". Please elaborate.
I don't think we disagree about the facts. I acknowledged in my previous message that uses of runtime qualified lexical bindings are like uses of the eval operator or with statements: they can be statically detected, so an implementation can optimize the cases where they are not found. What I meant by "defeating... &c." is simply that when they are found, an implementation cannot perform this optimization:
function foo() { var x = 5; var y = 25;
function bar(ns, n, v) { var x = 10; ns::[n] = v; } }
The computed lookup has the same effect as eval: every non-shadowed name in scope at the point where ns::[n] is evaluated is up for grabs. Only foo's x is safe, above. So, yes, a lookup of foo's x could be resolved ahead of time. (An implementation may or may not consider it worthwhile, though, to use hybrid environment representations.)
The point of contention, I think, is this:
-
eval has been changed in ES4 so that it would be possible to detect cases where lexical bindings cannot be resolved ahead of time. Lars called the design "an obvious attempt at killing 'eval' by a thousand cuts (yet remaining backward compatible!)" I took the comment to be suggesting that if backwards compatibility were not an issue, eval wouldn't be able to modify lexical bindings, at all.
-
Brendan proposed a reformed with statement that wouldn't interfere were lexical lookup the way the old with statement does.
Perhaps mistakenly, I took these as evidence that only backwards compatibility concerns are keeping ES4 from ensuring that all lexical bindings can be statically resolved. That's why I was surprised to find a new language feature that inhibits static analysis in a manner similar to old language features, which the designers are trying to kill.
-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Jon Zeppieri Sent: 21. mars 2008 09:49 To: Jeff Dyer Cc: Brendan Eich; es4-discuss Discuss Subject: Re: namespaces and expression-qualified names
On Fri, Mar 21, 2008 at 11:12 AM, Jeff Dyer <jodyer at adobe.com> wrote:
Names that can be resolved at compile time won't (can't) change meaning at runtime. And names that are qualified by constant namespaces that appear in the (safe) contexts you describe above can be resolved at compile-time. So I don't understand your charge of runtime qualifiers "defeating compile time lookup". Please elaborate.
I don't think we disagree about the facts. I acknowledged in my previous message that uses of runtime qualified lexical bindings are like uses of the eval operator or with statements: they can be statically detected, so an implementation can optimize the cases where they are not found. What I meant by "defeating... &c." is simply that when they are found, an implementation cannot perform this optimization:
function foo() { var x = 5; var y = 25;
function bar(ns, n, v) { var x = 10; ns::[n] = v; } }
The computed lookup has the same effect as eval: every non-shadowed name in scope at the point where ns::[n] is evaluated is up for grabs. Only foo's x is safe, above. So, yes, a lookup of foo's x could be resolved ahead of time. (An implementation may or may not consider it worthwhile, though, to use hybrid environment representations.)
The point of contention, I think, is this:
eval has been changed in ES4 so that it would be possible to detect cases where lexical bindings cannot be resolved ahead of time. Lars called the design "an obvious attempt at killing 'eval' by a thousand cuts (yet remaining backward compatible!)" I took the comment to be suggesting that if backwards compatibility were not an issue, eval wouldn't be able to modify lexical bindings, at all.
Brendan proposed a reformed with statement that wouldn't interfere were lexical lookup the way the old with statement does.
Perhaps mistakenly, I took these as evidence that only backwards compatibility concerns are keeping ES4 from ensuring that all lexical bindings can be statically resolved. That's why I was surprised to find a new language feature that inhibits static analysis in a manner similar to old language features, which the designers are trying to kill.
The problem with the old 'eval' (as implemented in Firefox at least) is that you can't tell when it is called, so any environment anywhere is potentially changed at run-time, and must also be able to respond to run-time lookups by names. Most people can live with an 'eval' that's visible; it's the invisible one that really hurts.
ns::[x] is like eval but it's visible. with is visible. These are slow and expensive constructs under some implementation techniques, but that's not the real problem, because they are mostly pay-as-you-go.
On Mar 21, 2008, at 9:48 AM, Jon Zeppieri wrote:
- Brendan proposed a reformed with statement that wouldn't interfere were lexical lookup the way the old with statement does.
Perhaps mistakenly, I took these as evidence that only backwards compatibility concerns are keeping ES4 from ensuring that all lexical bindings can be statically resolved. That's why I was surprised to find a new language feature that inhibits static analysis in a manner similar to old language features, which the designers are trying to kill.
You're spot on. Compatibility means we will have certain pain points,
probably forever. But adding another one is to be avoided without
super-compelling reasons.
Reformed with, which I suspect will be cut judging from the red on
its row at spreadsheets.google.com/pub?
key=pFIHldY_CkszsFxMkQOReAQ&gid=2, was intended to aid migration of
with-happy code to ES4, without losing all the benefits of strict
mode (never mind performance; my mantra lately is that dynamic
optimization techniques will speed JS up enough that types are really
for programming in the large). And there is a ton of with-happy code
out there, including web app and Ajax library code.
So I agree: let's not add more loopholes without strong
justification. And I'll wave the reformed with flag one last time,
for adding to the bad old loopholes where they can be reformed, if
not simply punishing them with a thousand cuts.
Are namespaces (and I'm not referring to the reflected objects here) first class values?
The overview document explicitly calls then "compile-time values," then proceeds to give the following example:
==== The following lines create namespace constants ns1, ns2, ns3, and ns4; the first two are anonymous (and equal), the last two have program-defined content (and are also equal):
====
The example -- specifically, the fact that I can alias a namespace -- suggests that these are first-class, runtime values.
Also, the grammar has an ExpressionQualifiedName non-terminal, which puts an expression in the namespace position of a lexical reference, like:
(foo.bar)::x
This, too, suggests that namespaces are first-class values.
On the other hand, the RI only agrees up to a point. This works:
5
5
This does not:
true
5
ns1 and ns2 are identical run-time values, but the latter cannot be used to qualify x.
Also, I can't seem to find a use of an ExpressionQualifiedName in the RI that won't result in an error. (This could just be a bug in the RI, of course.) How would an ExpressionQualifiedName be used?
I'm assuming the rationale behind the overview document's position (that namespaces are compile-time values) is that this is the only way to make lexical references resolvable at compile-time (ignoring eval and with for simplicity). I agree. But in that case, why have namespaces mimic first-class values up to a point? Why allow them on the right hand side of an assignment expression, at all? Here is another example of this kind of odd behavior (and, again, this may simply be due to bugs in the implementation):
2
Just to be clear, I'm not advocating first-class namespaces. I'm just curious why the language seems to pretend that they are first-class values.