ES5 left-to-right evaluation order issues
Since I talked about valueOf in my original message I'm going to interpret Oliver's comment as saying that even in ES3, without getter/setter extensions, you can still observed the separation of "evaluation" from "coercion". That's correct. In general, the way the ES3 (and 5) spec is written for binary operators of the form expr1 op expr2 any coercion of the value of expr1 types place after the evaluation of expr2. Since expr2 can potentially be an arbitrary expression that includes function calls, side-effects can be generated whi8le evaluating expr2 that are observed to occur before any side-effects from coercion (side-effects of valueOf, etc.) of expr1.
"Fixing" this would require substantial changes to the specification and to existing implementations that I doubt anyone wants to make.
So, the main point is that a belief that ECMAScript has or is supposed to have a strict left-to-right evaluation order (as the concept is likely to be understood by typical users of the language) is wrong. For consistency, Dave-Sarah's observation that we first "evaluate" and then "coerce" the operands is probably the guideline we should continue to follow if we ever define any additional operators where the distinction is relevant.
Allen Wirfs-Brock wrote:
This is good...Perhaps we should have a "design rules" section of the Wiki
to capture stuff like this?
I don't have edit rights to the wiki (the former ES4 wiki, that is, not the Trac wiki), and AFAIK neither does anyone else who is not a member of TC39.
We generally claim that ECMAScript uses a left-to-right evaluation order and in ES5 we even fixed what was consider a bug in the ES3 spec (second item ES5 Annex D) regarding evaluation order of relational operators. Consider:
var a={valueOf: function () {alert(" a valueOf");return 1}}; var b={valueOf: function () {alert(" b valueOf");return 2}}; a<=b;
A strict implementation of the ES3 spec. would alert: "b valueOf" followed by "a valueOf" indicating what appears to be a right-to-left evaluation order. Chrome actually does this while at least IE and FF alert "a valueOf" followed by "b valueOf". The ES5 changes say that "a valueOf" should occur first as this is consistent with the general left-to-right rule.
However, when accessor properties were added to the specification we did not consider the observable ordering interaction between accessor get functions with side-effects and ToPrimitive operations (ie valueOf) with side-effects. A typical binary operator algorithm from section 11 looks like:
Note that the two calls to GetValue precede the two calls to ToNumber. Generally in ES3 GetValue doesn't have side-effects so left-to-right order is maintained (however the ordering is observable if the right argument throws a ReferenceError). The interweaved evaluation order is much easier to observe if accessor properties are implemented. Consider executing the following in the context of the declarations of a and b above:
var o={get a() {alert("get a");return a}, get b() {alert ("get b");return b}}; o.a*o.b;
GetValue calls the accessor get functions, so based upon the above algorithm, you would expect to see the alert sequence: "get a", "get b", "a valueOf", "b valueOf". This is exactly what happens for existing getter/setter implementations in at least FF and Chrome.
So, the ES5 specification of operators is consistent with existing implementations of accessor properties. However, this is not a strict left-to-right order of evaluation order. We don't have many options at this point. Here are the alternatives I see:
It would take substantial work to fix this throughout the specification and actual implementation of the strict left-to-right rule would introduce numerous potentially observable changes to existing implementations. Based upon this, I suspect that we have to choose alternative #1 although I would personally be open to considering alternative #2.
Thoughts?