Like most Javascript programmers, I have tended to follow a simple rule for functions using 'this': eta-expand method selections, use .bind, or get into trouble.

Then I got curious about how method calls determine what
object to pass as 'this': a method is a function selected from an object, and functions are first-class values, so, by the time they get called, how do we know where they came from?

So I looked into the spec, and things deteriorated from there.

I would be interested in the rationale for the current specification of PropertyReferences, as it seems to invalidate a large class of
program equivalences (see below for examples).

Status:

According to the Ecmascript spec (11.2.1), property accessors return not the selected value but a Reference (8.7), which is a combination of the object selected from and the name of
the property being selected (the property value is not stored
immediately, but selected later when calling GetValue on such a PropertyReference).

Function calls (11.2.3) then construct 'this' from the object
in a PropertyReference, and the whole Reference concept,
as far as its use for 'this' is concerned, seems finely tuned to mimic a piece of syntax (conserve the base object info just long enough to use it for 'this'), rather than a semantic value (trying to pass around PropertyReferences is likely
to end up calling GetValue, losing the reference info).

Question 1: Should a Reference hold on to the current property value?

   Currently, there seems to be no way to store a property
Reference without GetValue getting called, so there is no window for changing the property value such a Reference
   refers to behind its back. That would no longer be true if
   References could be passed around, as language values.

Question 2:
   If a Reference allows us to recover 'obj' from 'obj.method',
why does this information have to get lost when passing it through a variable binding?

   obj.longish()    // correct 'this'
   var short = obj.longish; // try to define a shorthand
   short(); // oops, wrong 'this'

   This seems to be a very popular mistake - most beginners
   seem to get burned once. Naively, one thinks of selection
   losing the information, but that does not seem to be the
case. So could this error source be eliminated by passing the Reference as a value, instead of only the value component
   without the base object, one step further?

Question 3:
   It seems that trying to reuse References for 'this' forces
   early calls to GetValue (because users should not have to
call 'obj.method.valueOf()' or 'obj.property.valueOf()' to trigger the delayed selection, and because the property
   value is not stored in the PropertyReference).

This loses information that we would like to hold on to - if we really cannot solve this for References in general,
   why not store the 'this'-candidate in the function instead
   (similar to the fairly new '[[boundThis]]')?

   That might allow to limit the equivalence breakage with
   respect to determining 'this'.

Currently, searching for 'Reference' in the spec gives an uneven picture. For instance, the spec claims that The Reference Specification Type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators

However, References are also used to determine the value
of 'this' for function calls, and most expressions/operations/
variable bindings/function calls cannot pass through References, returning only their values instead. This information is spread over too many pages - it could be summarized in the section on References.

Question 4: (general version of question 2)
   Why is the origin information in References lost so easily?

   It seems that most parts of the spec require GetValue() by
   default, with few exceptions. What would go wrong if the
   available information would be passed on instead (isn't
   it sufficient for the final consumers to call GetValue(),
   provided that the original property value is stored in the
   PropertyReference, to avoid interference)?

Broken equivalences:

It is not too surprising that eta-conversion does not hold

   obj.method <-/-> function() { return obj.method(); }

although usually, the problems are with termination, side-effects, or type errors, while in this case, the problem
seems to be context-sensitive: many contexts treat References
differently. That can be confusing.

Currently, quite a few code transformations are not valid
if the code involves References and might be used in the
context of a function call (MemberExpression/CallExpression).
Here are some examples:

   var x = obj.m; x(); <-/-> obj.m();

   obj.m.valueOf() <-/-> obj.m

   (function(){ return obj.m; }()) <-/-> obj.m

(x = obj.m) <-/-> obj.m // where x is unused

[obj.m][0] <-/-> obj.m // obvious in hindsight? // but really surprising the first time

   {tmp: obj.m}.tmp <-/-> obj.m
       // this explains the one above

   (0,obj.m) <-/-> obj.m

   (true && obj.m) <-/-> obj.m

   (false || obj.m) <-/-> obj.m

(true ? obj.m : obj.m) <-/-> obj.m // Firefox 3.6.11 wrongly optimizes this one

I find the result very confusing: not only will a method
lose its Reference just by passing it around, but it will
pick up a new Reference by passing it through any kind of Object. This picking-up-new-Reference is probably
needed for mixins (copying methods from one object
to another), but it means that storing naked method References in Arrays is not recommended.

I was not aware that just about any code transformation
would be invalidated by the handling of References. If this cannot be fixed, could the specification be more explicit about this, please?
Claus


_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to