Existential operator
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
On Apr 13, 2011, at 11:37 AM, Dmitry A. Soshnikov wrote:
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined" && user.address != null) ? user.address.street : undefined;
(Just user.address != null would be enough, although perhaps there's a reason to typeof-test for undefined first that I am missing.)
The same with functions (which is even more convenient that just with properties):
let value = entity.getBounds?().direction?.x
which desugars into:
let x = (typeof entity.getBounds == "function") ? (typeof entity.getBounds().direction != "undefined" && entity.getBounds().direction != null) ? entity.getBounds().direction.x : undefined undefined;
(I specially avoid optimization with saving intermediate results -- just to keep clarity)
(Let's hope getBounds has no side effects :-P)
The ?. form from CoffeeScript is attractive at first glance. For notifying an ad-hoc observer where the reference to the observer may be null or undefined, it saves an 'if'. But for deeper optional control flow you need an 'if' anyway, in my experience.
How much is this used in CoffeeScript compared to regular .? Some crude measurement would be helpful.
See strawman:default_operator --
the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment.
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined" && user.address != null) ? user.address.street : undefined;
Part of what Dmitry asked for, I'd like to see in the plain ?: operator, and
it seems like it would be possible to disambiguate from a top-down parser
perspective. I would like to see the :
("else condition") portion of a ?:
expression be optional. For instance:
var a = b ? c; // aka, var a = b ? c : undefined
The other (more awkward/obscure looking) way to do this is:
var a; b && a = c;
The difference between the sytnax sugar I'm asking for and the "default
operator" in the strawman is that ?: (or &&) allows for separate expressions
for the test (b
) and the success_value (c
), whereas ?? requires that the
test expression and success_value be the same expression.
For instance:
var a = (b > 5) ? b : undefined;
In this case, the ?? "default operator" is of no use. But being able to drop
the : undefined
part, and also avoid using the more awkward looking &&
syntax, would certainly be a useful sugar.
On 4/13/11, Kyle Simpson <getify at gmail.com> wrote:
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment.
I have sometimes wanted something like that to avoid temporary variables.
The other (more awkward/obscure looking) way to do this is:
var a; b && a = c;
a = b && c;
Your suggestion to change the ternary operator is interesting but creates incompatibility. It is not feasible.
The other (more awkward/obscure looking) way to do this is:
var a; b && a = c;
a = b && c;
That is not the same thing. Your code assigns b
to a
if b
is falsy .
The other code either leaves a
as undefined (strictly doesn't assign) if
the test fails, or assigns it the value of c
(no matter what the type of
c
is) if the test succeeds.
Your suggestion to change the ternary operator is interesting but creates incompatibility. It is not feasible.
I'm curious what "incompatibility" you mean? If we're talking about backwards compatibility... of course. But a lot of the ES-Harmony (and later) stuff is of that same persuasion. I'm simply saying if we're talking about adding sugar to these operators for future versions of ES, this is one pattern I end up typing a LOT and it would be helpful.
Or is there some ambiguity of top-down parsing that I'm missing?
On Apr 13, 2011, at 3:38 PM, Kyle Simpson wrote:
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment.
This was a one-line FYI, and on-topic in reply to Dmitry's post since he brought up ?= (spelled ??= in the strawman). Why are you objecting to it?
Moving right along...
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined" && user.address != null) ? user.address.street : undefined;
Part of what Dmitry asked for, I'd like to see in the plain ?: operator, and it seems like it would be possible to disambiguate from a top-down parser perspective. I would like to see the
:
("else condition") portion of a ?: expression be optional. For instance:var a = b ? c; // aka,
var a = b ? c : undefined
The other (more awkward/obscure looking) way to do this is:
var a; b && a = c;
First, making : optional introduces a dangling-else ambiguity:
x = a ? b ? c : d;
This could be (x = a ? (b ? c : d)) or (x = a ? (b ? c) : d).
True, if-else already has this (traditional in C-based languages) ambiguity, "resolved" by associating : with the inner ? and so requiring the programmer to use braces if the other association is wanted, or in general just to avoid trouble. But why should we add more of the same kind of ambiguity, given the benefit of hindsight?
Second, as I see Garrett asked in followup, why not simply write:
var a = b && c;
If b is falsy but not undefined, and you don't want its falsy value propagating to a, then of course you'll have to write out:
var a = b ? c : undefined;
or:
var a; if (b) { a = c; }
or equivalent.
but this seems like a hard case, and as they teach first year law students, those make bad law.
The difference between the sytnax sugar I'm asking for and the "default operator" in the strawman is that ?: (or &&) allows for separate expressions for the test (
b
) and the success_value (c
), whereas ?? requires that the test expression and success_value be the same expression.For instance:
var a = (b > 5) ? b : undefined;
In this case, the ?? "default operator" is of no use. But being able to drop the
: undefined
part, and also avoid using the more awkward looking && syntax, would certainly be a useful sugar.
I think the dangling else problem is enough to nix this, but I welcome other comments. I'm also not sure b is falsy but not undefined in practice. It seems contrived to want a numeric value sometimes, and undefined others. IOW,
var a = (b > 5) ? b : undefined;
looks like a bug that will result in "undefined" or NaN values propagating via a, some of the time, into other numeric or number to string expressions. It looks like a mis-coded "min" against 5.
I don't see any ?. use case here, so I'm still not sure what this has to do with Dmitry's post or my reply. The topic in the Subject: line is CoffeeScript's existential operator, not anything that might be spelled with a ? -- but it's ok, we can discuss. Just please don't take me to task for citing a relevant strawman in reply to someone else's post that brought up (among several things) exactly what the strawman proposes, namely ??=.
On Apr 13, 2011, at 7:51 PM, Kyle Simpson wrote:
Your suggestion to change the ternary operator is interesting but creates incompatibility. It is not feasible.
I'm curious what "incompatibility" you mean? If we're talking about backwards compatibility... of course. But a lot of the ES-Harmony (and later) stuff is of that same persuasion.
You must mean forward compatibility (sometimes called "upward compatibility"), the ability of old runtimes to execute new code.
There is no backward compatibility issue ignoring code that fails due to early SyntaxErrors.
I'm simply saying if we're talking about adding sugar to these operators for future versions of ES, this is one pattern I end up typing a LOT and it would be helpful.
Could you cite examples where you code this a LOT? (or even a LITTLE ;-). Thanks,
On 13.04.2011 16:57, Brendan Eich wrote:
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
On Apr 13, 2011, at 11:37 AM, Dmitry A. Soshnikov wrote:
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined"&& user.address != null) ? user.address.street : undefined; (Just user.address != null would be enough, although perhaps there's a reason to typeof-test for undefined first that I am missing.)
Yes, != would be enough for property accessor. But the proposal assumes to test for existence of the global vars as well (where normally we would got ReferenceError):
let foo = bar?.baz
(regardless that ES6 will have compile-time bindings, it still possible
to create global var at runtime via this.bar
, though, if there will be
no global object, I'm not sure how this applies to the ES6)
The same with functions (which is even more convenient that just with properties):
let value = entity.getBounds?().direction?.x
which desugars into:
let x = (typeof entity.getBounds == "function") ? (typeof entity.getBounds().direction != "undefined"&& entity.getBounds().direction != null) ? entity.getBounds().direction.x : undefined undefined;
(I specially avoid optimization with saving intermediate results -- just to keep clarity) (Let's hope getBounds has no side effects :-P)
Yeah, right ;)
The ?. form from CoffeeScript is attractive at first glance. For notifying an ad-hoc observer where the reference to the observer may be null or undefined, it saves an 'if'. But for deeper optional control flow you need an 'if' anyway, in my experience.
Yes, of course for some complex if
s it's better to use if
s. However,
this:
if (foo.bar) { if (foo.bar.baz) { if (typeof foo.bar.baz.quax == "function") { foo.bar.baz.quax("value") } } }
is "too noisy" in contrast with:
foo.bar?.baz?quax("value");
and first of all, this syntactic sugar is proposed exactly this such cases.
How much is this used in CoffeeScript compared to regular .? Some crude measurement would be helpful.
Can't say precisely (and even roughly). But personally I used it pair of times, it was convenient.
Notice also, this form can be used even in this view:
let foo = bar? && baz? : true : false;
which tests for exactly existence of bar and baz.
And default values are also the case sure:
foo ?= default // or foo ?? default (no matter much)
Dmitry.
On 13.04.2011 17:38, Kyle Simpson wrote:
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).
The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment.
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined" && user.address != null) ? user.address.street : undefined;
Part of what Dmitry asked for, I'd like to see in the plain ?: operator, and it seems like it would be possible to disambiguate from a top-down parser perspective. I would like to see the
:
("else condition") portion of a ?: expression be optional. For instance:var a = b ? c; // aka,
var a = b ? c : undefined
Hm, intuitively the form a = b ? c
sounds for me as:
a = b ? b : c
Dmitry.
On 13.04.2011 21:57, Brendan Eich wrote:
On Apr 13, 2011, at 3:38 PM, Kyle Simpson wrote:
See strawman:default_operator -- the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:). The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment. This was a one-line FYI, and on-topic in reply to Dmitry's post since he brought up ?= (spelled ??= in the strawman). Why are you objecting to it?
Moving right along...
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined"&& user.address != null) ? user.address.street : undefined; Part of what Dmitry asked for, I'd like to see in the plain ?: operator, and it seems like it would be possible to disambiguate from a top-down parser perspective. I would like to see the
:
("else condition") portion of a ?: expression be optional. For instance:var a = b ? c; // aka,
var a = b ? c : undefined
The other (more awkward/obscure looking) way to do this is:
var a; b&& a = c; First, making : optional introduces a dangling-else ambiguity:
x = a ? b ? c : d;
This could be (x = a ? (b ? c : d)) or (x = a ? (b ? c) : d).
True, if-else already has this (traditional in C-based languages) ambiguity, "resolved" by associating : with the inner ? and so requiring the programmer to use braces if the other association is wanted, or in general just to avoid trouble. But why should we add more of the same kind of ambiguity, given the benefit of hindsight?
Yeah, right, no new ambiguities are needed.
Second, as I see Garrett asked in followup, why not simply write:
var a = b&& c;
If b is falsy but not undefined, and you don't want its falsy value propagating to a, then of course you'll have to write out:
var a = b ? c : undefined;
or:
var a; if (b) { a = c; }
or equivalent.
Yes, or (which today is used as a better alternative of the default value):
b && (a = c);
(though, this doesn't assign undefined
to c
in case of failur)
This approach is used to avoid unnecessary assignment to itself. Instead of:
foo = foo || default;
better replacement is:
foo || (foo = default);
Another example:
Array.prototype.map || Array.prototype.map = function () { /* map implementation */ };
And of course from this viewpoint special default operator (again, no matter much in which exactly syntactic form -- ??, ?=, ??=, ||=, etc) is good.
Dmitry.
On Apr 13, 2011, at 8:04 PM, Dmitry A. Soshnikov wrote:
var a = b ? c; // aka,
var a = b ? c : undefined
Hm, intuitively the form
a = b ? c
sounds for me as:a = b ? b : c
Which would be
a = b || c;
or if you want to test exactly b === undefined,
a = b ?? c;
but this brings us back to the strawman.
More in reply to your previous message about ?. and just ? as postfix operator.
On Apr 13, 2011, at 8:01 PM, Dmitry A. Soshnikov wrote:
(regardless that ES6 will have compile-time bindings, it still possible to create global var at runtime via
this.bar
, though, if there will be no global object, I'm not sure how this applies to the ES6)
No, Harmony won't alias this to the global scope (which is lexical, not an object). It'll be the WindowProxy as usual, in top level code. Dave has addressed what it will be in a module recently.
is "too noisy" in contrast with:
foo.bar?.baz?quax("value");
and first of all, this syntactic sugar is proposed exactly this such cases.
Did you leave out a . after the second . in that example?
I had dinner with Jeremy Ashkenas last month, and he testified that CoffeeScript's disamgibuator pass (between lexing and parsing) is a work in progress and a work of (literately programmed) random logic, which he has tweaked based on user bug reports, even recently.
This is not something to put into ECMA-262. So a postfix ? operator seems right out as a general operator, given ?: and ??/??=.
How much is this used in CoffeeScript compared to regular .? Some crude measurement would be helpful.
Can't say precisely (and even roughly). But personally I used it pair of times, it was convenient.
Notice also, this form can be used even in this view:
let foo = bar? && baz? : true : false;
which tests for exactly existence of bar and baz.
OTOH, if ? as an identifier suffix, no space allowed, were added to Harmony, then we would have a migration tax in the form of an early error, which might be ok:
// old code, note lack of space between a and ? x = a? b : c;
Harmony treating ? as an identifier suffix as an existential test would then find the run-on fragment "b : c;" a syntax error.
This still seems a bit shaky. Is it important compared to ?. and part of the "same package deal"?
On Apr 13, 2011, at 8:25 PM, Brendan Eich wrote:
is "too noisy" in contrast with:
foo.bar?.baz?quax("value");
and first of all, this syntactic sugar is proposed exactly this such cases.
Did you leave out a . after the second . in that example?
Er, after the second ? in that example?
OTOH, if ? as an identifier suffix, no space allowed, were added to Harmony, then we would have a migration tax in the form of an early error, which might be ok:
I'd find that a bit astonishing since other punctuators (like prefix "-" and "+" and postfix ":") don't work that way. If "?" did become a no-space-allowed identifier suffix, it might be nice to do the same thing for ":" property names in object literals since that would let you use reserved words as property names (or labels, I suppose) without complicating the parser:
{ if: 123 } // ok since "if:" becomes a single PropertyName("if") token and not IfToken followed by ColonToken.
I declare this whole no-space-suffix idea a "bad path" and renounce it.
Again, CoffeeScript is its own (informally specified) language (Jeremy is awesome). However: we should learn from it but not blindly copy from it.
On 13.04.2011 22:25, Brendan Eich wrote:
On Apr 13, 2011, at 8:01 PM, Dmitry A. Soshnikov wrote:
(regardless that ES6 will have compile-time bindings, it still possible to create global var at runtime via
this.bar
, though, if there will be no global object, I'm not sure how this applies to the ES6) No, Harmony won't alias this to the global scope (which is lexical, not an object).
Yeah, quite a normal implementation. But I'm curious how will it be possible to address a dynamically formed property name -- the name of the global var?
this["foo" + bar]
It'll be the WindowProxy as usual, in top level code. Dave has addressed what it will be in a module recently.
I have to look on Dave's explanation, seems I missed it. But this WindowProxy won't be assessable then, right? Will it be possible to define a new global property/variable at all at runtime depending on condition?
is "too noisy" in contrast with:
foo.bar?.baz?quax("value");
and first of all, this syntactic sugar is proposed exactly this such cases. Did you leave out a . after the second . in that example?
Oh, yes of course, sorry.
I had dinner with Jeremy Ashkenas last month, and he testified that CoffeeScript's disamgibuator pass (between lexing and parsing) is a work in progress and a work of (literately programmed) random logic, which he has tweaked based on user bug reports, even recently.
You mean it was hard for Coffee to implement this foo?.bar? etc?
This is not something to put into ECMA-262. So a postfix ? operator seems right out as a general operator, given ?: and ??/??=.
Sorry, I didn't get from these two sentences -- whether postfix ? is good or not? :D (really sorry, just a translation hardness).
How much is this used in CoffeeScript compared to regular .? Some crude measurement would be helpful.
Can't say precisely (and even roughly). But personally I used it pair of times, it was convenient.
Notice also, this form can be used even in this view:
let foo = bar?&& baz? : true : false;
which tests for exactly existence of bar and baz. OTOH, if ? as an identifier suffix, no space allowed, were added to Harmony, then we would have a migration tax in the form of an early error, which might be ok:
// old code, note lack of space between a and ? x = a? b : c;
Hm, not good. The whole ambiguity of this construct with casual a ? : c already makes it not good for me. And by the way, Coffee didn't have ? : it uses inline if - then instead.
(well, actually it's possible to write in Coffee a = b ? b : c, but it compiles into completely different semantics).
Harmony treating ? as an identifier suffix as an existential test would then find the run-on fragment "b : c;" a syntax error.
Yes, I got it.
This still seems a bit shaky. Is it important compared to ?. and part of the "same package deal"?
Don't know, not so seems. And by the way, in my view, the operator is not "?." (i.e. "a question followed by a dot"), but still just ?. The following dot is already about property accessor:
foo.bar?.baz
again, bar? is separated, and only after that it's accessed to baz
via
casual dot notation.
Dmitry.
It'll be the WindowProxy as usual, in top level code. Dave has addressed what it will be in a module recently.
I have to look on Dave's explanation, seems I missed it. But this WindowProxy won't be assessable then, right? Will it be possible to define a new global property/variable at all at runtime depending on condition?
I think what Brendan means is that legacy script (that doesn't opt in to the new version) will continue to be ES5 and will continue to use the window object as the global scope frame. But the window won't be the global environment record in Harmony.
In Harmony (as you know, Dmitry), the global scope is separate from the window object. At Harmony top-level, |this| is bound to an object that reflects the global scope, but it's not possible to do things like delete bindings from it, and it's a separate object from the window object.
See strawman:default_operator --
the proposal there is ?? and ??= since single ? is ambiguous after an expression due to conditional expressions (?:).The "default operator" doesn't address a significant part of what Dmitry is asking for -- the . in the ?. usage -- which allows the property access to be expressed only once and used for both the test and assignment.
This was a one-line FYI, and on-topic in reply to Dmitry's post since he brought up ?= (spelled ??= in the strawman). Why are you objecting to it?
I apologize, I thought you were citing the ??/??= strawman in reference to Dmitry's original first question, about the ?. operator. I didn't realize you were instead referring to his second question, about ?=.
I don't see any ?. use case here, so I'm still not sure what this has to do with Dmitry's post or my reply. The topic in the Subject: line is CoffeeScript's existential operator, not anything that might be spelled with a ?
Yes, I apologize for slightly hijacking the thread. I was really just making
an aside note that part of what Dmitry was asking for, which the ?. in
Coffeescript does -- allowing the trailing : undefined
part to be
omitted -- is something that I indeed find useful in and of itself, and in
fact would like to see it on the basic ?: operator, if possible.
First, making : optional introduces a dangling-else ambiguity:
x = a ? b ? c : d;
This could be (x = a ? (b ? c : d)) or (x = a ? (b ? c) : d).
True, if-else already has this (traditional in C-based languages) ambiguity, "resolved" by associating : with the inner ? and so requiring the programmer to use braces if the other association is wanted, or in general just to avoid trouble. But why should we add more of the same kind of ambiguity, given the benefit of hindsight?
I'm not sure I see how this is really introducing an additional ambiguity? As you rightly assert, this "ambiguity" is already present when you chain a series of nested ?: usages together, and you already sometimes have to use () to dis-ambiguate, something which is long since familiar to those who dare to brave the ?: nested chaining. It seems like it's the same ambiguity, not additional. But I suppose that's just a matter of perspective.
For x = a ? b ? c : d
, It seems pretty reasonable and based on existing
precedent with ?: operator precedence that it'd be taken as x = a ? (b ? c : d) {: undefined}
. If that's what you wanted, then it's not ambiguous,
and you don't need (). If you want the other way, you simply use () and make
it so. Not sure why this would be bad additional precedent?
I personally tend to avoid that pattern of coding, as I find the potential for mishaps greater than the benefit. But in the times where I do use such a pattern, I'm cautious to always use (), even when not strictly necessary, so in that respect, there'd be no ambiguity to using ?: with optional :, at least in the way I code things. And I don't think adding () to some chains where you want to override operator precedence is an undue hardship on anyone, as you already (sometimes) have to do that with ?:.
I think the dangling else problem is enough to nix this,
That's a shame. I hope not. But I suppose I'm not surprised if it turns out to be so.
I'm also not sure b is falsy but not undefined in practice. It seems contrived to want a numeric value sometimes, and undefined others. IOW,
var a = (b > 5) ? b : undefined;
looks like a bug that will result in "undefined" or NaN values propagating via a, some of the time, into other numeric or number to string expressions. It looks like a mis-coded "min" against 5.
In my code, one example where I often use a pattern of something either
being undefined or having a real value, is in "options" object
configurations, like when you pass an options hash to a function. For
instance, if a property is omitted, or it's present but is undefined
, then
it's taken to have not been set at all, and thus is either ignored, or in
some cases is defaulted to some other value. OTOH, if it's set to an actual
numeric value, then the numeric value is of course used.
The value null
is another common value used for the purpose of indicating
"not set" or "ignore this property". I tend to not like that quite as much,
since typeof null == "object"
(which can be confused with other types if
you're not careful) where as typeof undefined == "undefined"
unambiguously.
There's also been a few cases where I've distinguished between a value being
"undefined" (aka, "not set") and the value being "null" (aka, set
deliberately to empty). For instance, if you pass in an option with the
value as undefined
, that means "not set" and it's ok to grab and use a
default value for that option. But if you explicitly pass in an option with
value null
, that means "disable or ignore me" and don't use the default
value. I don't use "false" in this case, as it's easy to mistakingly coerce
that to a 0 numeric value.
In any case, null
vs. undefined
aside, having sugar for this pattern
would just be nice:
var opts = { doX: (someX > 0 && someX < 10) ? someX , // leaving off the `:
undefined(or
: null` if you prefer)
doY: (someY > 0 && someY < 1) ? someY // ditto
};
doSomething(opts);
function doSomething(opts) { (typeof opts.doX == "undefined") && opts.doX = 5; (typeof opts.doY == "undefined") && opts.doY = 0.3; (typeof opts.doZ == "undefined") && opts.doZ = "foobar";
if (opts.doX != null) { ... } if (opts.doY != null) { ... } if (opts.doZ != null) { ... } }
On Apr 13, 2011, at 8:46 PM, Dmitry A. Soshnikov wrote:
I had dinner with Jeremy Ashkenas last month, and he testified that CoffeeScript's disamgibuator pass (between lexing and parsing) is a work in progress and a work of (literately programmed) random logic, which he has tweaked based on user bug reports, even recently.
You mean it was hard for Coffee to implement this foo?.bar? etc?
Not really given Coffee's lack of ?: as ternary operator.
But in general, we can't harvest syntax from CoffeeScript without bottom-up, ASI-aware grammar validation.
It is easy to go wrong.
Hm, not good. The whole ambiguity of this construct with casual a ? : c already makes it not good for me. And by the way, Coffee didn't have ? : it uses inline if - then instead.
(well, actually it's possible to write in Coffee a = b ? b : c, but it compiles into completely different semantics).
Right!
in my view, the operator is not "?." (i.e. "a question followed by a dot"), but still just ?. The following dot is already about property accessor:
foo.bar?.baz
again, bar? is separated, and only after that it's accessed to
baz
via casual dot notation.
?. is doable as a new operator, but we need a better quantification of how useful it is in CS.
On Apr 13, 2011, at 9:21 PM, Kyle Simpson wrote:
First, making : optional introduces a dangling-else ambiguity:
x = a ? b ? c : d;
This could be (x = a ? (b ? c : d)) or (x = a ? (b ? c) : d).
True, if-else already has this (traditional in C-based languages) ambiguity, "resolved" by associating : with the inner ? and so requiring the programmer to use braces if the other association is wanted, or in general just to avoid trouble. But why should we add more of the same kind of ambiguity, given the benefit of hindsight?
I'm not sure I see how this is really introducing an additional ambiguity?
It is obviously introducing an ambiguity where none exists today. ?: is indivisible, unlike if vs. if else.
I'm also not sure b is falsy but not undefined in practice. It seems contrived to want a numeric value sometimes, and undefined others. IOW,
var a = (b > 5) ? b : undefined;
looks like a bug that will result in "undefined" or NaN values propagating via a, some of the time, into other numeric or number to string expressions. It looks like a mis-coded "min" against 5.
In my code, one example where I often use a pattern of something either being undefined or having a real value, is in "options" object configurations, like when you pass an options hash to a function. For instance, if a property is omitted, or it's present but is
undefined
, then it's taken to have not been set at all, and thus is either ignored, or in some cases is defaulted to some other value. OTOH, if it's set to an actual numeric value, then the numeric value is of course used.The value
null
is another common value used for the purpose of indicating "not set" or "ignore this property". I tend to not like that quite as much, sincetypeof null == "object"
(which can be confused with other types if you're not careful) where astypeof undefined == "undefined"
unambiguously.There's also been a few cases where I've distinguished between a value being "undefined" (aka, "not set") and the value being "null" (aka, set deliberately to empty). For instance, if you pass in an option with the value as
undefined
, that means "not set" and it's ok to grab and use a default value for that option. But if you explicitly pass in an option with valuenull
, that means "disable or ignore me" and don't use the default value. I don't use "false" in this case, as it's easy to mistakingly coerce that to a 0 numeric value.
In sum, this sounds like an argument against ? as infix operator (implied : undefined).
In any case,
null
vs.undefined
aside, having sugar for this pattern would just be nice:var opts = { doX: (someX > 0 && someX < 10) ? someX , // leaving off the
: undefined
(or: null
if you prefer) doY: (someY > 0 && someY < 1) ? someY // ditto };
Sorry, I think this is a hazardous pattern. "doX" suggests a boolean value, but you want (number | undefined), a type union. If any consumer fails to discriminate using typeof, they'll get undefined which coerces to NaN as number (to 0 as integer). Bad times.
On Apr 13, 2011, at 9:06 PM, David Herman wrote:
It'll be the WindowProxy as usual, in top level code. Dave has addressed what it will be in a module recently.
I have to look on Dave's explanation, seems I missed it. But this WindowProxy won't be assessable then, right? Will it be possible to define a new global property/variable at all at runtime depending on condition?
I think what Brendan means is that legacy script (that doesn't opt in to the new version) will continue to be ES5 and will continue to use the window object as the global scope frame. But the window won't be the global environment record in Harmony.
Ok, or maybe I was just out of date ;-).
In Harmony (as you know, Dmitry), the global scope is separate from the window object. At Harmony top-level, |this| is bound to an object that reflects the global scope, but it's not possible to do things like delete bindings from it, and it's a separate object from the window object.
Cool -- is this spec'ed yet?
Cool -- is this spec'ed yet?
On 13.04.2011 23:06, David Herman wrote:
It'll be the WindowProxy as usual, in top level code. Dave has addressed what it will be in a module recently. I have to look on Dave's explanation, seems I missed it. But this WindowProxy won't be assessable then, right? Will it be possible to define a new global property/variable at all at runtime depending on condition? I think what Brendan means is that legacy script (that doesn't opt in to the new version) will continue to be ES5 and will continue to use the window object as the global scope frame. But the window won't be the global environment record in Harmony.
In Harmony (as you know, Dmitry), the global scope is separate from the window object. At Harmony top-level, |this| is bound to an object that reflects the global scope, but it's not possible to do things like delete bindings from it, and it's a separate object from the window object.
Yeah, thanks for reminding, Dave. I quote it from the spec-draft (for others):
"The initial binding of |this| at program top-level is a prototype-less, non-extensible, non-configurable reflection of the global environment record. Programs can get and set the global bindings through this object, but cannot add or remove bindings. "
So this means, no function expressions depending on the condition? I.e.:
this["foo"] = isDebug ? function () { ... } : function () { ... }
Or I guess, such cases will be replaced with function statements, right?
if (isDebug) { function debug() { ... } } else { function debug() { ... } }
However, no dynamic function creation anymore (and actually, any binding). I just remember nice snippets such as:
['Object', 'Array', 'String', 'Function',
'RegExp'].forEach(function (constructorName) { this['is' + constructorName] = function (object) { return Object.prototype.toString.call(object) == '[object '
- constructorName + ']'; }; });
which allowed me to create several test-function such as "isString", "isArray", etc.
Nothing of this anymore in ES6, right?
And another quote from there:
"The initial binding of |this| at module top-level is the module instance object for that module."
Just wanted to correspond this with the recent class-definition discussion, when I also proposed that "this-value evaluated in the class body -- is the class itself", which allows to define elegantly class/static methods.
Dmitry.
Think of it this way: dynamic binding no, dynamic assignment yes.
So this means, no function expressions depending on the condition? I.e.:
this["foo"] = isDebug ? function () { ... } : function () { ... }
var foo = isDebug ? function() { ... } : function() { ... }
Or I guess, such cases will be replaced with function statements, right?
if (isDebug) { function debug() { ... } } else { function debug() { ... } }
Sure, that would be fine too.
However, no dynamic function creation anymore (and actually, any binding).
Of course you can still do dynamic function creation. You just can't do dynamic binding.
I just remember nice snippets such as:
['Object', 'Array', 'String', 'Function', 'RegExp'].forEach(function (constructorName) { this['is' + constructorName] = function (object) { return Object.prototype.toString.call(object) == '[object ' + constructorName + ']'; }; });
which allowed me to create several test-function such as "isString", "isArray", etc.
Nothing of this anymore in ES6, right?
For the most part, this will not be possible. You can do it in a phased way, by creating the globals dynamically via the module loader before evaluating the script that wants to use them. E.g.:
['Object', 'Array', 'String', 'Function', 'RegExp'].forEach(function(constructorName) {
ModuleLoader.defineGlobal('is' + constructorName, function(object) {
return Object.prototype.toString.call(object) == '[object ' + constructorName + ']';
});
});
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval.
Or I guess, such cases will be replaced with function statements, right?
if (isDebug) { function debug() { ... } } else { function debug() { ... } }
Sure, that would be fine too.
Er, sorry, no -- block-local function declarations will be block-local in Harmony.
Dmitry A. Soshnikov :
The existential operator is a syntactic sugar to avoid long testing whether a property exists and only after that to apply it. This already is again used in CoffeeScript, so I'll show the examples:
let street = user.address?.street
It seems useful for testing, but also it is a little bit cryptic code. Personally I would not read code full with existential operators. Could you provide use case of:
entity.getBounds?().direction?.x
In your code I don't think you will need from existential operator.
entity.getBounds().direction.x;
It will be enough and it is definitely easier for debugging. The existential operators would be really helpful for feature detections.
Instead of question mark I prefer to use different property access notation. For example:
entity->getBounds()->direction->x ;
It will solve the problem with the ambiguous syntax.
On Apr 14, 2011, at 7:43 PM, Asen Bozhilov wrote:
It will solve the problem with the ambiguous syntax.
There is no ambiguity problem with ? followed by . and then (with whitespace allowed, of course) an IdentifierName. We need the lookahead to an identifier-name starting-character to avoid this:
var tolerance = big ?.1:.01;
So we can't simply maximum-munch '?.'. But ?. is doable with some care, and probably better spelling than -> (which does not connote the conditionality as ? does).
As an aside: In previous discussions, I've seen these patterns somewhere (possibly on jsmentors):
( ( obj || {} ).prop1 || {} ).prop2
try { return obj.prop1.prop2; } catch(e) { return undefined; }
The first doesn't handle errors, the second might handle too many (the second is also a statement, not an expression).
With #-functions offering a shorthand for delayed evaluation, one might even define (with the same caveat about exceptions):
function safely(_x) { // exception-safe evaluation of delayed _x
try { return _x(); } catch(e) { return undefined; }
}
.. safely( #(){ obj.prop1.prop2 } )
As a meta point, it may be useful to consider why these operators cannot be defined as the need for them arises. Unless the operators are considered harmful, having to ask for a language extension often highlights serious shortcomings in the language:
- no user-defined infix operators..
workaround: none really, though we can try to emulate
infix ops through object methods
- all functions evaluate their arguments by default,
and there isn't even a way to change this default
workaround:
e --> #(){ e } // delay evaluation of e
e --> e() // force evaluation of delayed e
This workaround has the well-known problem that
all call-sites have to be modified. A way to specify
delays on formal parameters instead would help.
Possible approximation of intended infix operator definition (I'd be very happy to see a better approximation!):
// no 'user' defined var user1 = {}; var user2 = { address : {} }; var user3 = { address : { street : "lonely" }};
// wrap obj to allow safe property access, // with (_qd) and without (qd) safe delayed evaluation function $(obj) { return { qd : function(prop) { return (obj || {})[prop]; } , _qd: function(prop) { try { return (obj() || {})[prop]; } catch(e) { return undefined; }; } }; }
// who wants to code like this, even if it works?
// delay dangerous phrases, then wrap and select log( typeof $($(#(){ user })._qd('address')).qd('street') );
// just wrap and select log( typeof $($(user1).qd('address')).qd('street') ); log( typeof $($(user2).qd('address')).qd('street') ); log( typeof $($(user3).qd('address')).qd('street') );
Can we do any better with Harmony, without changing the language for every useful operator? If we cannot do better, should the language just keep growing, should users make do without their operators, or should the shortcomings be fixed?-)
Claus
On 2011-04-13, at 15:23, Brendan Eich wrote:
in my view, the operator is not "?." (i.e. "a question followed by a dot"), but still just ?. The following dot is already about property accessor:
foo.bar?.baz
again, bar? is separated, and only after that it's accessed to
baz
via casual dot notation.?. is doable as a new operator
And ?( as an operator, too?
, but we need a better quantification of how useful it is in CS.
Or look for the patterns it would simplify in JS? Seems like a lot of these patterns come from duck-typing, from "continue down this path if it applies".
On 2011-04-14, at 11:55, John J. Barton wrote:
Perhaps there is no better solution, but often I find that I want to say "call this chain of functions and use the bottom value, but if any of them return undefined, then just be undefined, don't get all pissed off and throw an exception. Being undefined is how JavaScript is." I was imagining that this was the feature being discussed.
This is the way AS2 worked. If at any point an undefined value was hit (whether property access or call), the evaluation stopped and undefined was returned. The experience of OpenLaszlo was that this led to many myserious bugs. We ended up making our compiler insert annotations in debug mode to detect and warn when an expression failed due to an undefined value. That experience makes me believe that the current behavior in ES is a better default; but having a shorthand for "stop now if this is undefined", seems like it would be a nice addition.
On 14.04.2011 20:42, David Herman wrote:
Think of it this way: dynamic binding no, dynamic assignment yes.
Yes, I've already learned it testing modules.
Though, should say that it will decrease the beauty JS definitions as runtime expressions. The price? -- interpreter efficiency of course (direct lexical scope mapping of bindings, O(1) for resolving) and early-compile-time errors. But as was said, languages first of all are for humans, not for machines, so in chaise of efficiency we shouldn't turn the language into less convenient for exactly programmers. But, it's just a side phylosopical note ;) Moreover, I also cannot say that dynamic bindings are so often case, so the efficiency of course matters.
So this means, no function expressions depending on the condition? I.e.:
this["foo"] = isDebug ? function () { ... } : function () { ... } var foo = isDebug ? function() { ... } : function() { ... }
Yeah, right, but not dynamic names anymore.
Or I guess, such cases will be replaced with function statements, right?
if (isDebug) { function debug() { ... } } else { function debug() { ... } } Sure, that would be fine too.
You said (in the next letter) that it will be a local (like the let- ones) binding. Seems, it will "break" previous semantics. Though, again, today the best practice is to avoid function statements at all, because of difference in implementation; only SpiderMonkey has the sensible one IMO. And in SM's it's a outer scope binding, not the inner.
However, no dynamic function creation anymore (and actually, any binding). Of course you can still do dynamic function creation. You just can't do dynamic binding.
Yes, yes, but which decreases elegance of definitions but increases "intermediate noise" (though, maybe not so noise?).
I just remember nice snippets such as:
['Object', 'Array', 'String', 'Function', 'RegExp'].forEach(function (constructorName) { this['is' + constructorName] = function (object) { return Object.prototype.toString.call(object) == '[object ' + constructorName + ']'; }; });
which allowed me to create several test-function such as "isString", "isArray", etc.
Nothing of this anymore in ES6, right? For the most part, this will not be possible. You can do it in a phased way, by creating the globals dynamically via the module loader before evaluating the script that wants to use them. E.g.:
['Object', 'Array', 'String', 'Function', 'RegExp'].forEach(function(constructorName) { ModuleLoader.defineGlobal('is' + constructorName, function(object) { return Object.prototype.toString.call(object) == '[object ' + constructorName + ']'; }); });
Hm, defineGlobal
? It's interesting.
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval.
Which again seems as in chaise of efficiency, let's take away (a little of) convenience from a user.
Anyway, thanks for clarifications, Dave.
Dmitry.
On Thu, Apr 14, 2011 at 3:29 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval.
Which again seems as in chaise of efficiency, let's take away (a little of) convenience from a user.
This isn't about efficiency at all!
The reason that Dave and I have worked hard to make modules lexically scoped, of which this is a part, is so that when you look at a program, you can understand what it does. This is a fundamental part of programming, and not having a clear binding structure in your language makes it much harder.
Sometimes, this makes your program more efficient, too, because the compiler understands your program better for the same reasons that you can understand your program more easily. But that really isn't the point.
Dynamic binding is bad, mmmkay? ;)
Seriously, it's not an efficiency thing. Dynamic scope is easy to write but hard to predict. JS is lexically/statically scoped almost everywhere, except for with, eval, and the global object. Strict mode solves with and eval. Harmony solves the global object.
You said (in the next letter) that it will be a local (like the let- ones) binding. Seems, it will "break" previous semantics.
No, it doesn't. The previous semantics doesn't include local functions. That's a non-standard extension.
Yes, yes, but which decreases elegance of definitions but increases "intermediate noise" (though, maybe not so noise?).
I dare you to call dynamic bindings elegant to any of my friends. ;)
On 14.04.2011 22:00, Brendan Eich wrote:
On Apr 14, 2011, at 7:43 PM, Asen Bozhilov wrote:
It will solve the problem with the ambiguous syntax. There is no ambiguity problem with ? followed by . and then (with whitespace allowed, of course) an IdentifierName. We need the lookahead to an identifier-name starting-character to avoid this:
var tolerance = big ?.1:.01;
Damn, it's a mind-bender :D Really hard to get what's going on in this expression. It's a good for quiz; thanks for the example.
More human-view version (for interested): var tolerance = big ? 0.1 : 0.01;
Dmitry A. Soshnikov:
which in sugar form is:
var vars = context.getPanel?("html", true).getInspectorVars();
I don't think this is real use case. It seems like a design mistake. Why would you check for existence "html" panel? If the "html" panel does not exist in the Firebug something will be wrong in the whole application. With code like this, they will need more time to debug and fixing.
The same case would be:
document.getElementById('someID')?.getAttribute('someAttr');
Again I don't see any reasons to check for existence that element. If it does not exist something is definitely wrong with my application.
Maybe I am missing something, but existential operator will solve some design mistakes. I don't think that the language should fix script authors mistakes.
.
Dynamic binding is bad, mmmkay? ;)
Seriously, it's not an efficiency thing. Dynamic scope is easy to write but hard to predict. JS is lexically/statically scoped almost everywhere, except for with, eval, and the global object. Strict mode solves with and eval.
Isn't there another culprit that breaks static binding (without introducing a new form of dynamic binding, though)?
Function.toString() does not account for closures, or any static bindings really, rendering all variables by name. We can thus use toString to break static binding, followed by eval to rebind dynamically.
This is kind of expected for bindings in the function body, but kind of odd for non-local bindings.
In the example below, static binding would suggest that x,y, and world are bound to the global vars, even in the function parameter to withE, right? But toString unbinds them.
Claus
"use strict";
function log(msg) { if (typeof console!=='undefined') console.log(msg); else if (typeof WScript!=='undefined') WScript.Stdout.WriteLine(msg); }
// this isn't really 'with', but .. function withE(env,body) { var code = ""; for(var e in env) { if (env.hasOwnProperty(e)) code += "var "+e+"="+env[e]+";"; } code += body.toString().replace(/^function\s*([^)])\s/,"");
log(code);
return eval(code); }
var x = "with", y = "hello", world = "world";
var result = withE({x:"'"+y+"'",y:"'"+x+"'",hello:"'"+y+"'"} ,function(hello){ x+", "+y+"! "+hello+", "+world+"!"; });
log(result);
On Apr 14, 2011, at 8:59 PM, P T Withington wrote:
On 2011-04-13, at 15:23, Brendan Eich wrote:
in my view, the operator is not "?." (i.e. "a question followed by a dot"), but still just ?. The following dot is already about property accessor:
foo.bar?.baz
again, bar? is separated, and only after that it's accessed to
baz
via casual dot notation.?. is doable as a new operator
And ?( as an operator, too?
That would seem to want a bottom up parser. Consider
(a?(b instanceof c):d)
vs.
(a?(b instanceof c))
A top-down parser could simulate by parsing ahead assuming ?:, and in the second case on finding no : where expected, revise the AST for the prefix that was already parsed. This looks nasty, and it needs more rigorous validation.
It is also future-hostile to using : in new ways that might start an expression -- probably not an issue but I'll throw it out.
This is the way AS2 worked. If at any point an undefined value was hit (whether property access or call), the evaluation stopped and undefined was returned. The experience of OpenLaszlo was that this led to many myserious bugs. We ended up making our compiler insert annotations in debug mode to detect and warn when an expression failed due to an undefined value.
This is compelling testimony. I didn't know that about AS2.
That experience makes me believe that the current behavior in ES is a better default; but having a shorthand for "stop now if this is undefined", seems like it would be a nice addition.
Agree on first part.
"[N]ice addition" depends on making the syntax usable and easy to parse.
On Apr 14, 2011, at 5:19 PM, Brendan Eich wrote:
On Apr 14, 2011, at 8:59 PM, P T Withington wrote:
On 2011-04-13, at 15:23, Brendan Eich wrote:
in my view, the operator is not "?." (i.e. "a question followed by a dot"), but still just ?. The following dot is already about property accessor:
foo.bar?.baz
again, bar? is separated, and only after that it's accessed to
baz
via casual dot notation.?. is doable as a new operator
And ?( as an operator, too?
That would seem to want a bottom up parser. Consider
(a?(b instanceof c):d)
vs.
(a?(b instanceof c))
A top-down parser could simulate by parsing ahead assuming ?:, and in the second case on finding no : where expected, revise the AST for the prefix that was already parsed. This looks nasty, and it needs more rigorous validation.
I'm not convinced this would be too difficult to handle, at least in a hand written parser.
That said I'm not endorsing the syntax as I think that historically languages that are "hard" to parse are also hard to understand, I'm just commenting on implementation difficulty.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20110414/2592af54/attachment
On Apr 15, 2011, at 2:39 AM, John J Barton wrote:
P T Withington wrote:
This is the way AS2 worked. If at any point an undefined value was hit (whether property access or call), the evaluation stopped and undefined was returned. The experience of OpenLaszlo was that this led to many myserious bugs. We ended up making our compiler insert annotations in debug mode to detect and warn when an expression failed due to an undefined value. That experience makes me believe that the current behavior in ES is a better default; but having a shorthand for "stop now if this is undefined", seems like it would be a nice addition. [snip]
So you could make the argument that the Dmitry thing ought to work with the debugger. Rather than a mysterious 'undefined' you should have a a way to rapidly ask the debugger to tell you: which link in the chain is 'undefined'. And that is what you did in your compiler: a good solution to your problem but not an argument against the syntax IMO.
Seems to me Tucker was pretty clearly arguing that ?. should be explicit as in CoffeeScript, not implicit and inevitable via . as in AS2. That's all.
I agree with Oliver, we can probably make ?. and ?( work in JS.
CoffeeScript has it easy on several counts beyond not having ?: in the first place:
- single open source implementation, the code is the spec.
- rapid evolution by user-testing, no formal grammar.
The last could be considered a hardship, not having it easy.
In any event, suffice to say that ECMA-262 can't follow this methodology. We need multiple browser engine implementors to buy in. We need a formal spec that's sound and valid first.
It would be good to hear from spec gurus and more implementors.
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20110414/43a8d155/attachment
On 4/13/11, Kyle Simpson <getify at gmail.com> wrote:
The other (more awkward/obscure looking) way to do this is:
var a; b && a = c;
a = b && c;
That is not the same thing. Your code assigns
b
toa
ifb
is falsy . The other code either leavesa
as undefined (strictly doesn't assign) if the test fails, or assigns it the value ofc
(no matter what the type ofc
is) if the test succeeds.
That is true. So you want a
undefined iff b
is falsy. I'm not sure
I'd need that but perhaps in that case:
var a = b && c || a;
Reads easier grouped (to me). var a = (b && c) || a;
John J Barton:
The HTML tab you see in the Firebug tab set is just a picture, there is no real HTML panel.
All of Firebug's panels are actually <div> elements allocated on demand. So the code above is saying:
If the user has ever used the HTML panel in debugging this web page, then get the inspector vars.
In that case shouldn't be call getInspectorVars
when the HTML has
been showed? It is really questionable how this design is the best
approach.
Wait! If the language can fix my mistakes then I'm definitely all for it!
The existential operator reminds me for PHP @
operator. Over two
weeks ago I have to deal with PHP application, which should be moved
on another server. When I moved the application to the new server I
got simply white screen as output of my browser. I was walking trough
the included files and I found in db.php
a line:
@mysql_pconnect();
It was coded if mysql_pconnect does not return value which can be converted to true to die the script.
The time for debugging that was over 1 hour and the problem was that I haven't got installed mysql library. I really don't want to debugging such a code.
Personally if I have to use the existential operator I will start thinking about better design for my application.
.
On Apr 14, 2011, at 20:48, Brendan Eich <brendan at mozilla.com> wrote:
Seems to me Tucker was pretty clearly arguing that ?. should be explicit as in CoffeeScript, not implicit and inevitable via . as in AS2. That's all.
Yes. Our experience was that implicitly short-circuiting on dereferencing undefined had benefits, but was more trouble than it was worth. I want the best of both worlds — an error when I don't expect undefined, and a way to explicitly permit short-circuiting on undefined. A syntax like Dmitry's proposal would make duck typing and lazy allocation patterns more succinct.
— Sent from my device that I decline to endorse blindly. Nor do I request absolution for any typographical sins.
On 4/15/2011 5:09 AM, Asen Bozhilov wrote:
John J Barton:
The HTML tab you see in the Firebug tab set is just a picture, there is no real HTML panel.
All of Firebug's panels are actually<div> elements allocated on demand. So the code above is saying:
If the user has ever used the HTML panel in debugging this web page, then
get the inspector vars. In that case shouldn't be call
getInspectorVars
when the HTML has been showed? It is really questionable how this design is the best approach.
That is what this code does, it calls getInspectorVars only if the HTML panel has been used.
Wait! If the language can fix my mistakes then I'm definitely all for it! The existential operator reminds me for PHP
@
operator. Over two weeks ago I have to deal with PHP application, which should be moved on another server. When I moved the application to the new server I got simply white screen as output of my browser. I was walking trough the included files and I found indb.php
a line:@mysql_pconnect();
It was coded if mysql_pconnect does not return value which can be converted to true to die the script.
Then this example is not related to the issue being discussed. In the
issue being discussed, the caller already knows that the result can be
'undefined' and that any call in the chain may result in 'undefined'.
Other cases are already supported.
The time for debugging that was over 1 hour and the problem was that I haven't got installed mysql library. I really don't want to debugging such a code.
As in the other example, syntax can't fix bugs.
Personally if I have to use the existential operator I will start thinking about better design for my application.
I don't think anyone proposes make this operator mandatory.
jjb
On 4/14/11, Brendan Eich <brendan at mozilla.com> wrote:
There is no ambiguity problem with ? followed by . and then (with whitespace allowed, of course) an IdentifierName. We need the lookahead to an identifier-name starting-character to avoid this:
var tolerance = big ?.1:.01;
So we can't simply maximum-munch '?.'.
What about square-bracket property accessors? Is [propertyName] an Array or is it the new conditional property access with square-brackets? var p = o.v ? [propertyName];
On 14.04.2011 23:40, Sam Tobin-Hochstadt wrote:
On Thu, Apr 14, 2011 at 3:29 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval. Which again seems as in chaise of efficiency, let's take away (a little of) convenience from a user. This isn't about efficiency at all!
The reason that Dave and I have worked hard to make modules lexically scoped, of which this is a part, is so that when you look at a program, you can understand what it does. This is a fundamental part of programming, and not having a clear binding structure in your language makes it much harder.
Sometimes, this makes your program more efficient, too, because the compiler understands your program better for the same reasons that you can understand your program more easily. But that really isn't the point.
Actually, ECMAScript is already lexically scoped language (in respect of closures). I think there's a mess with treating a possibility of creating a binding at runtime with the dynamic scope. Dynamic scope (bit.ly/hz7tT2 -- just for interested) is an early design flow in PLT backing to Lisp invention times (where language authors have no much experience). Perl e.g. still has both -- dynamic and static scopes. But JS has/had lexical scope.
Yes, with
and eval
(direct, non-strict) are the features which
brings dynamics to the static scope of ES. And I always thought (and it
seems quite logical) that the direct lexical mapping of ids (which is
the reason of disallowing of creating of runtime bindings) is exactly to
improve the identifier resolution and technically it's the main reason.
Avoiding object to represent a scope and avoiding a scope chain
consisting of these object is the reason of providing direct map of
"variable name : direct address" which (theoretically?) is accessed in O(1).
But, I think it's a kind of off-topic in this thread, though, feel free to talk on it (probably in PM) if there's a need ;)
Dmitry.
On 14.04.2011 23:48, David Herman wrote:
Dynamic binding is bad, mmmkay? ;)
Mmmkay, Mr. Mackey :P But the main reason is increasing of performance. Of course, the understanding of your bindings (i.e. which vars are present in the program) is also the reason, but it seems manageable.
Seriously, it's not an efficiency thing. Dynamic scope is easy to write but hard to predict. JS is lexically/statically scoped almost everywhere, except for with, eval, and the global object. Strict mode solves with and eval. Harmony solves the global object.
Yeah.
You said (in the next letter) that it will be a local (like the let- ones) binding. Seems, it will "break" previous semantics. No, it doesn't. The previous semantics doesn't include local functions. That's a non-standard extension.
Yes, I know it's non-standard, but anyway the old code (at least with the most logical approach in SpiderMonkey if people used them) will be broken. But, let's hope a percentage of such uses is small, since, repeat, currently the best practice is to avoid them.
Dmitry.
On Apr 15, 2011, at 9:32 PM, Garrett Smith wrote:
On 4/14/11, Brendan Eich <brendan at mozilla.com> wrote:
There is no ambiguity problem with ? followed by . and then (with whitespace allowed, of course) an IdentifierName. We need the lookahead to an identifier-name starting-character to avoid this:
var tolerance = big ?.1:.01;
So we can't simply maximum-munch '?.'. What about square-bracket property accessors? Is [propertyName] an Array or is it the new conditional property access with square-brackets? var p = o.v ? [propertyName];
Same as the ?() case:
x = a ? [ b ] : c; y = d ? [ e ];
(or equivalent expressions where ) instead of ; stops the ? expression from being a ?: expression).
The fact that ? and the bracket are separate tokens here makes me think twice about treating ?. as one token with a lookahead restriction on identifer-name start-character following the dot.
My hunch is TC39 members won't agree on this but again, hearing from them would be better, and a worked-out proposal that doesn't make anyone too sick, even better.
On Fri, Apr 15, 2011 at 3:40 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
On 14.04.2011 23:40, Sam Tobin-Hochstadt wrote:
On Thu, Apr 14, 2011 at 3:29 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval.
Which again seems as in chaise of efficiency, let's take away (a little of) convenience from a user.
This isn't about efficiency at all!
The reason that Dave and I have worked hard to make modules lexically scoped, of which this is a part, is so that when you look at a program, you can understand what it does. This is a fundamental part of programming, and not having a clear binding structure in your language makes it much harder.
Sometimes, this makes your program more efficient, too, because the compiler understands your program better for the same reasons that you can understand your program more easily. But that really isn't the point.
Actually, ECMAScript is already lexically scoped language (in respect of closures). I think there's a mess with treating a possibility of creating a binding at runtime with the dynamic scope. Dynamic scope (bit.ly/hz7tT2 -- just for interested) is an early design flow in PLT backing to Lisp invention times (where language authors have no much experience). Perl e.g. still has both -- dynamic and static scopes. But JS has/had lexical scope.
Yes,
with
andeval
(direct, non-strict) are the features which brings dynamics to the static scope of ES. And I always thought (and it seems quite logical) that the direct lexical mapping of ids (which is the reason of disallowing of creating of runtime bindings) is exactly to improve the identifier resolution and technically it's the main reason. Avoiding object to represent a scope and avoiding a scope chain consisting of these object is the reason of providing direct map of "variable name : direct address" which (theoretically?) is accessed in O(1).
- The global object (which is what's originally under discussion here) also gives you "dynamic scope" for top-level variables.
window.fooish = 7 7 fooish 7
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all. I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason".
On Apr 15, 2011, at 9:56 PM, Sam Tobin-Hochstadt wrote:
- The global object (which is what's originally under discussion here) also gives you "dynamic scope" for top-level variables.
Right -- this is important and it is a deoptimer for optimizing implementations, but they can cope. The real problem is for JS hackers who cannot be sure their foopey typo (when they meant foopy) won't resolve to something else, accidentally or maliciously.
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all. I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason".
Well, you've said it, that's enough on its face ;-).
Developers I talk to really want early error on typos, it's a no-brainer.
Dmitry, if you mean something other than revoking this win of Harmony lexical scoping, please correct me. Otherwise, I do not see any good in reinjecting the global object (directly or via some kind of proxy) onto the scope chain.
On Apr 15, 2011, at 10:06 PM, Brendan Eich wrote:
On Apr 15, 2011, at 9:56 PM, Sam Tobin-Hochstadt wrote:
- The global object (which is what's originally under discussion here) also gives you "dynamic scope" for top-level variables.
Right -- this is important and it is a deoptimer
"deoptimizer"
for optimizing implementations, but they can cope.
It would be even better to not have to cope, so don't get me wrong: efficiency win is win.
Anyway, I agree that the main win (your self-proclaimed reason) is to make lexical scope go all the way up and avoid typo bugs, dynamic property collisions, and worse confusions.
On Fri, Apr 15, 2011 at 4:08 PM, Brendan Eich <brendan at mozilla.com> wrote:
for optimizing implementations, but they can cope.
It would be even better to not have to cope, so don't get me wrong: efficiency win is win.
I totally agree with this: win is win. And often optimization wins come from things that make programs easier to understand for both the programmer and the compiler, which is exactly the case here.
On 15.04.2011 23:56, Sam Tobin-Hochstadt wrote:
On Fri, Apr 15, 2011 at 3:40 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
On 14.04.2011 23:40, Sam Tobin-Hochstadt wrote:
On Thu, Apr 14, 2011 at 3:29 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
But these globals won't be accessible to scripts that have already been compiled; only to scripts that are subsequently compiled and evaluated, e.g. via ModuleLoader.eval. Which again seems as in chaise of efficiency, let's take away (a little of) convenience from a user. This isn't about efficiency at all!
The reason that Dave and I have worked hard to make modules lexically scoped, of which this is a part, is so that when you look at a program, you can understand what it does. This is a fundamental part of programming, and not having a clear binding structure in your language makes it much harder.
Sometimes, this makes your program more efficient, too, because the compiler understands your program better for the same reasons that you can understand your program more easily. But that really isn't the point. Actually, ECMAScript is already lexically scoped language (in respect of closures). I think there's a mess with treating a possibility of creating a binding at runtime with the dynamic scope. Dynamic scope (bit.ly/hz7tT2 -- just for interested) is an early design flow in PLT backing to Lisp invention times (where language authors have no much experience). Perl e.g. still has both -- dynamic and static scopes. But JS has/had lexical scope.
Yes,
with
andeval
(direct, non-strict) are the features which brings dynamics to the static scope of ES. And I always thought (and it seems quite logical) that the direct lexical mapping of ids (which is the reason of disallowing of creating of runtime bindings) is exactly to improve the identifier resolution and technically it's the main reason. Avoiding object to represent a scope and avoiding a scope chain consisting of these object is the reason of providing direct map of "variable name : direct address" which (theoretically?) is accessed in O(1).
- The global object (which is what's originally under discussion here) also gives you "dynamic scope" for top-level variables.
window.fooish = 7 7 fooish 7
OK, what I was saying is that you mixing the terminology calling as "dynamic scope" the "runtime bindings". And I mentioned that the dynamic scope is when a binding is resolved in the respect to the callee, but not in respect of the definition place (i.e. lexical place in the source code).
var foo = 10;
function bar() { console.log(foo); }
(function () { var foo = 20; bar(); // 10 in static scope, 20 in dynamic scope! })();
But I'm of course agree that avoiding of runtime bindings and allowing only of static bindngs (i.e. those names which directly are resolved at compile time and therefore the direct map of their addresses can be built -- to avoid scope chain lookup) is good -- from both sides -- to make these errors as early and (as usually it is) O(1) bindings resolution approach without any scope chain lookup.
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all.
Yes, it's OK, I got it.
I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason".
Oh, it's not required, I got it. And I also won't persuade you, but just suggest this resource mitpress.mit.edu/sicp/full-text/book/book-Z-H-35.html#%_sec_5.5.6 (the whole book is also recommended to be read if not yet).
Dmitry.
On Fri, Apr 15, 2011 at 4:32 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
OK, what I was saying is that you mixing the terminology calling as "dynamic scope" the "runtime bindings". And I mentioned that the dynamic scope is when a binding is resolved in the respect to the callee, but not in respect of the definition place (i.e. lexical place in the source code).
var foo = 10;
function bar() { console.log(foo); }
(function () { var foo = 20; bar(); // 10 in static scope, 20 in dynamic scope! })();
I understand how dynamic scope works. And ES5 with the mutable global object has it:
function bar() { return foo }; bar() // error: foo is not defined var foo = 7; bar(); // produces 7
Lexical scope would produce the error both times.
But I'm of course agree that avoiding of runtime bindings and allowing only of static bindngs (i.e. those names which directly are resolved at compile time and therefore the direct map of their addresses can be built -- to avoid scope chain lookup) is good -- from both sides -- to make these errors as early and (as usually it is) O(1) bindings resolution approach without any scope chain lookup.
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all.
Yes, it's OK, I got it.
I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason".
Oh, it's not required, I got it. And I also won't persuade you, but just suggest this resource mitpress.mit.edu/sicp/full-text/book/book-Z-H-35.html#%_sec_5.5.6 (the whole book is also recommended to be read if not yet).
I first read SICP a long time ago :)
On 16.04.2011 0:06, Brendan Eich wrote:
On Apr 15, 2011, at 9:56 PM, Sam Tobin-Hochstadt wrote:
- The global object (which is what's originally under discussion here) also gives you "dynamic scope" for top-level variables. Right -- this is important and it is a deoptimer for optimizing implementations, but they can cope. The real problem is for JS hackers who cannot be sure their foopey typo (when they meant foopy) won't resolve to something else, accidentally or maliciously.
Sure, this is the issue, though this is already done (in strict mode)
but as a runtime error; thus, dynamic bindings are kept (i.e it's
possible to this["foo" + bar] = 10
). Direct lexical addressing taking
way the ability of this["foo" + bar] = 10
provides those errors (of
nonExisting = 10
) to be compile-time error. That's it. But the issue
which Sam and you mention is already solved. So that's why I said that
the most important is the ability of the fast lookup.
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all. I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason". Well, you've said it, that's enough on its face ;-).
Developers I talk to really want early error on typos, it's a no-brainer.
Right.
Dmitry, if you mean something other than revoking this win of Harmony lexical scoping, please correct me. Otherwise, I do not see any good in reinjecting the global object (directly or via some kind of proxy) onto the scope chain.
Nope, everything's fine and correct, I just was thinking (from what it's all started) that we'll not be able to expressively define some bindings directly in runtime depending on the condition, etc (as was shown in reply to David with isString, isArray, etc functions). And then we switch to the "what's the main reason?".
But of course to have all those errors as compile-time is a good improvement. And! Which allows to really optimize identifiers resolution (which IMO, ok, -- if not the first for you, but the one of the most important reasons of such an optimization) :P
Dmitry.
On Fri, Apr 15, 2011 at 4:45 PM, Sam Tobin-Hochstadt <samth at ccs.neu.edu>wrote:
On Fri, Apr 15, 2011 at 4:32 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
OK, what I was saying is that you mixing the terminology calling as "dynamic scope" the "runtime bindings". And I mentioned that the dynamic scope is when a binding is resolved in the respect to the callee, but not in respect of the definition place (i.e. lexical place in the source code).
var foo = 10;
function bar() { console.log(foo); }
(function () { var foo = 20; bar(); // 10 in static scope, 20 in dynamic scope! })();
I understand how dynamic scope works. And ES5 with the mutable global object has it:
function bar() { return foo }; bar() // error: foo is not defined var foo = 7; bar(); // produces 7
Lexical scope would produce the error both times.
Hi Sam, this does establish that ES5 does not have lexical scope. But I don't how ES5 has dynamic scope. In the above example, the 'foo' that bar has in scope during the second call still has nothing to do with what's in scope at the call site from which bar is called.
On Fri, Apr 15, 2011 at 4:53 PM, Mark S. Miller <erights at google.com> wrote:
I understand how dynamic scope works. And ES5 with the mutable global object has it:
function bar() { return foo }; bar() // error: foo is not defined var foo = 7; bar(); // produces 7
Lexical scope would produce the error both times.
Hi Sam, this does establish that ES5 does not have lexical scope. But I don't how ES5 has dynamic scope. In the above example, the 'foo' that bar has in scope during the second call still has nothing to do with what's in scope at the call site from which bar is called.
I think what you're saying is that if I then do:
(function (){ var foo = 8; return bar(); })();
I'll get 7, not 8, which is certainly true. We can think of this as dynamically scoped with respect to "top-level" variables, ie those bound at the top level or unbound at the time of definition, or we can think of this as a variety of scoping that is neither lexical nor dynamic -- that's just a terminology question.
On 16.04.2011 0:59, Sam Tobin-Hochstadt wrote:
On Fri, Apr 15, 2011 at 4:53 PM, Mark S. Miller<erights at google.com> wrote:
I understand how dynamic scope works. And ES5 with the mutable global object has it:
function bar() { return foo }; bar() // error: foo is not defined var foo = 7; bar(); // produces 7
Lexical scope would produce the error both times. Hi Sam, this does establish that ES5 does not have lexical scope. But I don't how ES5 has dynamic scope. In the above example, the 'foo' that bar has in scope during the second call still has nothing to do with what's in scope at the call site from which bar is called. I think what you're saying is that if I then do:
(function (){ var foo = 8; return bar(); })();
I'll get 7, not 8, which is certainly true. We can think of this as dynamically scoped with respect to "top-level" variables, ie those bound at the top level or unbound at the time of definition, or we can think of this as a variety of scoping that is
neither lexical nor dynamic -- that's just a terminology question.
A "runtime scope augmentation" which still keeps the lexical scope (of functions in particular and mainly). But, yeah, I agree that we go into terminology discussion which is not the best.
Let's agree on this: of course lexical bindings addressing is the best in respect of (a) optimized identifiers lookup and (b) having these errors as compile-time, not runtime, but (2) we lose the ability to expressively define some bindings. Is this approach correct now? ;)
Dmitry.
On 16.04.2011 0:45, Sam Tobin-Hochstadt wrote:
On Fri, Apr 15, 2011 at 4:32 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
OK, what I was saying is that you mixing the terminology calling as "dynamic scope" the "runtime bindings". And I mentioned that the dynamic scope is when a binding is resolved in the respect to the callee, but not in respect of the definition place (i.e. lexical place in the source code).
var foo = 10;
function bar() { console.log(foo); }
(function () { var foo = 20; bar(); // 10 in static scope, 20 in dynamic scope! })(); I understand how dynamic scope works. And ES5 with the mutable global object has it:
function bar() { return foo }; bar() // error: foo is not defined var foo = 7; bar(); // produces 7
Lexical scope would produce the error both times.
Just a small nit-picking: (1) you shouldn't use var
, (2) lexical
addressing will produce the only compile-time error on "foo is not
defined" (since you write it as just foo = 7
).
But I'm of course agree that avoiding of runtime bindings and allowing only of static bindngs (i.e. those names which directly are resolved at compile time and therefore the direct map of their addresses can be built -- to avoid scope chain lookup) is good -- from both sides -- to make these errors as early and (as usually it is) O(1) bindings resolution approach without any scope chain lookup.
- Again, the reason that Dave and I have worked hard to avoid holes in the lexical scoping of identifiers in modules is for programmers to understand programs -- not for efficiency at all. Yes, it's OK, I got it.
I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason". Oh, it's not required, I got it. And I also won't persuade you, but just suggest this resource mitpress.mit.edu/sicp/full-text/book/book-Z-H-35.html#%_sec_5.5.6 (the whole book is also recommended to be read if not yet). I first read SICP a long time ago :)
Well, then you should know that the mentioned above chapter from the book is exactly about what I was saying, i.e., again -- "let's make lexical addressing to make fast lookup".
Dmitry.
On Fri, Apr 15, 2011 at 5:10 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason".
Oh, it's not required, I got it. And I also won't persuade you, but just suggest this resource mitpress.mit.edu/sicp/full-text/book/book-Z-H-35.html#%_sec_5.5.6 (the whole book is also recommended to be read if not yet).
I first read SICP a long time ago :)
Well, then you should know that the mentioned above chapter from the book is exactly about what I was saying, i.e., again -- "let's make lexical addressing to make fast lookup".
- Just because I've studied SICP doesn't mean I agree with everything they say there.
- The point of that section is describing an optimization that is possible, given lexical scope. They are certainly not advocating for lexical scope because it enables the optimization.
On 16.04.2011 1:17, Sam Tobin-Hochstadt wrote:
On Fri, Apr 15, 2011 at 5:10 PM, Dmitry A. Soshnikov <dmitry.soshnikov at gmail.com> wrote:
I don't know what else to say to persuade you of this, but making identifier resolution fast is not "the main reason". Oh, it's not required, I got it. And I also won't persuade you, but just suggest this resource mitpress.mit.edu/sicp/full-text/book/book-Z-H-35.html#%_sec_5.5.6 (the whole book is also recommended to be read if not yet). I first read SICP a long time ago :) Well, then you should know that the mentioned above chapter from the book is exactly about what I was saying, i.e., again -- "let's make lexical addressing to make fast lookup".
- Just because I've studied SICP doesn't mean I agree with everything they say there.
- The point of that section is describing an optimization that is possible, given lexical scope. They are certainly not advocating for lexical scope because it enables the optimization.
OK, let it be.
P.S.: so having this issue as already solved (as a runtime error, but still -- the error!, not a typo-hazard!) made me think that the main reason was another (as it's usually follows from having lexical addressing). OK, I propose to conclude at this point since we've found out the reasons.
Dmitry.
I've noticed that there are at least two uses of dynamic scope, all maledictions (and so tending to blur together into one general curse):
-
Using the dynamic link or call stack as scope chain link (clearly evil; er, eval ;-).
-
Having a scope chain element be a mutable object in which bindings can come and even go (also bad, and the issue here in ES5 non-strict and older JS).
We could try to call the latter "dynamic binding", but "binding" is so horribly overused and overloaded informally...
Not sure this helps :-(.
The fact is that "dynamic scope" is used to mean multiple things: 1) the "stack-like" semantics employed by e.g. the original Lisps, and 2) any non-static scoping semantics. The former was so famous that it came to be the common usage of the term, but #1 is really just a special case of #2. I've seen people use it both ways. Sam's using it the second way, Dmitry is using it the first way (and trying to claim that Sam's way is wrong). It's not really a deep issue, just ambiguous terminology.
P.S.: so having this issue as already solved (as a runtime error, but still -- the error!, not a typo-hazard!)
It doesn't always produce a runtime error. If the property happens to be there, there's no error. Also, take a look at the confusing issues that arise from the dynamic semantics of global lookup:
https://mail.mozilla.org/pipermail/es5-discuss/2010-October/thread.html#3760
Strict mode tried valiantly to make unbound variables an error, but there's only so much you can do with a dynamic semantics for scoping [1]. Once we have static scoping these problems go away.
Dave
[1] How's that for avoiding the phrase "dynamic scoping"? ;)
On Fri, Apr 15, 2011 at 6:28 PM, David Herman <dherman at mozilla.com> wrote:
The fact is that "dynamic scope" is used to mean multiple things: 1) the "stack-like" semantics employed by e.g. the original Lisps, and 2) any non-static scoping semantics. The former was so famous that it came to be the common usage of the term, but #1 is really just a special case of #2. I've seen people use it both ways. Sam's using it the second way, Dmitry is using it the first way (and trying to claim that Sam's way is wrong). It's not really a deep issue, just ambiguous terminology.
.#1 has such a long history and is such a clear concept that I really wish to preserve our ability to talk about it clearly. Why dynamic scoping was attractive and why it turns out to be bad is one of the most important lessons from the history of language design. For me "dynamic scoping" will always be #1. For #2, "non-static scope" is adequate and perfectly clear.
On Fri, Apr 15, 2011 at 6:35 PM, David Herman <dherman at mozilla.com> wrote:
P.S.: so having this issue as already solved (as a runtime error, but still -- the error!, not a typo-hazard!)
It doesn't always produce a runtime error. If the property happens to be there, there's no error. Also, take a look at the confusing issues that arise from the dynamic semantics of global lookup:
mail.mozilla.org/pipermail/es5-discuss/2010-October/thread.html#3760
Strict mode tried valiantly to make unbound variables an error, but there's only so much you can do with a dynamic semantics for scoping [1]. Once we have static scoping these problems go away.
Dave
[1] How's that for avoiding the phrase "dynamic scoping"? ;)
+1 ;).
I'd argue that the things that are wrong with "dynamic scope" in the Lisp sense (usage #1) are just as wrong with "non-static scope" (usage #2). Dynamic scope in Lisp was anti-modular because the meaning of a variable could be affected by any code throughout the arbitrary control flow of the program. The same is true whenever you have variables whose bindings are decided by dynamic control flow. Lisp's dynamic binding was just a special case of the general phenomenon of variables whose bindings are determined dynamically. This is why, in my mind, the distinction isn't actually all that important.
Regardless, it's clear I caused confusion by my usage. I'll make an effort on es-discuss to be explicit about which I mean.
I'd argue that the things that are wrong with "dynamic scope" in the Lisp sense (usage #1) are just as wrong with "non-static scope" (usage #2). Dynamic scope in Lisp was anti-modular because the meaning of a variable could be affected by any code throughout the arbitrary control flow of the program. The same is true whenever you have variables whose bindings are decided by dynamic control flow. Lisp's dynamic binding was just a special case of the general phenomenon of variables whose bindings are determined dynamically. This is why, in my mind, the distinction isn't actually all that important.
Perhaps the example below helps: it contrives to emulate dynamic scoping by dynamic (global) binding, complete with emulating local, stack-backed bindings, using temporary (global) bindings plus static stack.
Claus
function log(msg) { if (typeof console!=='undefined') console.log(msg); else if (typeof WScript!=='undefined') WScript.Stdout.WriteLine(msg); }
function B(l) { log(l+': x is '+x); // x is always global, but may not exist there; // // that still isn't static scoping; it is "just" // a question of whether x is bound, not where // it is bound, but the answer is not static. // // (if x isn't there, that is a reference error, // not an undefined) }
if (Math.random()>0.5) this.x = "hi"; // now you see it, now you don't // dynamic binding with global scope
if (typeof x=="undefined") {
this.x = "ho"; B("local"); // emulate dynamic scoping by dynamic binding delete this.x;
} else { // x is defined
B("global");
(function(){ var old_x = x; // use lexical stack to backup x delete x; // shadow x
try { B("gone 1"); } catch(e) { log('x no longer defined'); }
x = "temporary"; // emulate a local binding by
// a temporary global binding
B("dynamic"); // dynamic scoping, in all but name
delete x; // local/temporary binding ends
try { B("gone 2"); } catch(e) { log('x still not defined'); }
x = old_x; // restore x
}());
}
B("main");
On 16.04.2011 2:28, David Herman wrote:
The fact is that "dynamic scope" is used to mean multiple things: 1) the "stack-like" semantics employed by e.g. the original Lisps, and 2) any non-static scoping semantics. The former was so famous that it came to be the common usage of the term, but #1 is really just a special case of #2. I've seen people use it both ways. Sam's using it the second way, Dmitry is using it the first way (and trying to claim that Sam's way is wrong). It's not really a deep issue, just ambiguous terminology.
Yeah, it's not a big issue, as I also mentioned, just a terminology ambiguity. Moreover, I also mention in my article that "dynamic scope" definition can be applied in the context.
Once again (to conclude it):
A static (lexical) scope means that we can determine by looking on the source text in which environment frame an identifier will be resolved at further usage in runtime. This determination is made by the place of the identifier definition.
Actually, from this definition already may follow the fact that if the
program allows runtime scope augmentation (i.e. adding new bindings in
runtime) it has not only static scope, since using eval
, with
and
new runtime bindings of the global object don't allow us to say
precisely in which scope the binding will be resolved
(bit.ly/gWAULX , this is also to show that I also used
terminology of "dynamic scope" but adding the word "feature").
And since "classical" (?) dynamic scope has its own meaning, probably "runtime scope augmentation" is a better definition. Though, if everyone in discussion is aware about both application of this term, it's OK.
Notice, this runtime scope augmentation doesn't cancel the fact that the scope is still static (lexical). But if we have the possibility of this runtime scope augmentation then we cannot make the optimization with lexical addressing and need to use runtime scope chain lookup. However having avoided these runtime new bindings, we do have this optimization -- which is was my main point.
OK, let's conclude on the terminology debates (we've found out the truth and understood each other) and return to the very first topic -- do we need for all that this existential operator or not?
Having this thread read I already think that it makes sense in this operator only if we can to support all its features and only it it can be done easily in respect of parsers, unambiguous syntax constructs, etc.
OTOH, taking into account this talk on eliminating of the global object with imposibility to define a new global binding at runtime cancels the feature of just testing a variable -- since all variables exists before the code is executed.
I.e. there's no need in this:
if foo? { /* foo exists, do this */ }
Though, it's only on first glance, since if we take e.g. two combined files, then such a check can make sense (the the check sounds as "are we combined with another file which has the needed binding?":
if Widget? { /* we have this module */ }
Dmitry.
Le 19/06/2012 11:40, Hemanth H.M a écrit :
As there is no keyword as 'or' so far, does something like *x = x.value or 5 *sound better?
I realize I gave examples with default values in case of error, but it doesn't have to be the case. Specifically in
let greedy = try obj.hints.greedy
an "or" statement would be cumbersome since there is already 'undefined' as implicit or-value. But why not.
(I separate it from recent thread on shared handlers for proxies).
The existential operator is a syntactic sugar to avoid long testing whether a property exists and only after that to apply it. This already is again used in CoffeeScript, so I'll show the examples:
let street = user.address?.street
which desugars e.g. into:
street = (typeof user.address != "undefined" && user.address != null) ? user.address.street : undefined;
The same with functions (which is even more convenient that just with properties):
let value = entity.getBounds?().direction?.x
which desugars into:
let x = (typeof entity.getBounds == "function") ? (typeof entity.getBounds().direction != "undefined" && entity.getBounds().direction != null) ? entity.getBounds().direction.x : undefined undefined;
(I specially avoid optimization with saving intermediate results -- just to keep clarity)
I think it's useful thing and I already used in several times. Do we need it in ES6? It's convenient.
Another examples (which already as I know were planed for ES6, such as ||= or ?=, etc):
score ?= 0
desugars into (notice, there's no unnecessary assignment as in score = score || 0; also, this sugar assumes that
score
may not even exists):(typeof score != "undefined" && score != null) : score ? (score = 0);
Normally, it can be done (assuming that
score
exists):score || (score = 0);
so score ?= 0 is really just a sugar, no more, no less.
Dmitry.