The behavior of References isn't as arbitrary or different from other languages as it might seem.
It's really a way to specify l-values.

When you assign to an object property, e.g., "o.x = 42", the l-value here is the "x" property of
the "o" object. We need to capture both.

In other languages, e.g., C or Java, you have the same problem:
  int x = something.prop;
  x = 10;
is not the same as
  something.prop = 10;
In C/C++, you know that the something.prop on the right-hand side of an assignment
means something slightly different from the one on the left-hand side.
We have the same thing in ECMAScript:
  var x = something.prop;
  x = 10;
is not the same as
  something.prop = 10;

So far, References as a specification mechanism is just following other languages, and behaving exactly as any seasoned programmer would expect. Try checking your questions
against the expected behavior if a Reference is just an l-expression.


Where it differs is function/method calls. The traditional languages that I compared to does not have functions as first-class values. If a function sits on an object,
you can't extract it and call it without such an object.

Well, there is C++ method pointers which stay bound to the object they were extracted from (but don't try to guess the size of one, they are probably bigger than whatever you might think is necessary for that). And they are different from pointers to static
functions.

The binding of "this" when calling a Reference value mimics method calls. It does so fine when you treat objects as objects, but not when you try to extract a method from its object (try doing that in Java!). It's a shallow abstraction, but it does work when you play along
with it.


A Reference is purely a specification-tool that desn't have to exist in any form inside an actual implementation. If we start exposing it, we would require implementations to take steps they might not need in order to visibly create and pass around such a reference.

If you really need user-level references, you can create them yourself, and just do
  var ref = new Reference(object, "prop");
  var val = ref.GetValue();
  ref.SetValue(val + 10);
  ref.SetValue(someFunction);
  ref.call(arg1, arg2);

/L

On Mon, 11 Apr 2011 10:55:49 +0200, Claus Reinke <claus.rei...@talk21.com> wrote:

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.

Which value? The one the property (if it existed) had when the reference
was created? Or the current one - if the Reference survives for any amount
of time, the object property could change its value in the meantime.
What if it's getter property?
What if it's a setter property with not getter?

If you make a reference a first-class value, then you probably don't want
to make too many assumptions about how it's used. Don't read a value from it
unless the user wants to do so.

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?

Ofcourse it's possible, but personally I prefer to have the "this" object
obvious in the call line. That way I know what object the method is being
called on. Without it, the loss of context is in the source code, making it
harder to read and maintain.


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).

I'm not sure I understand what the problem is here.


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]]')?

Won't work. The same function can be used in many places at the same time.
E.g.
 [obj1.foo, obj2.foo][(Math.random() * 2) | 0]();


    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)?

References is a specification tool. If it survived for an extended amount of time, and visibly so, implementations would have to actually implement something to represent it. As it is now, a reference is found and immediately consumed, which allows implementations to never create it at all, and work directly on the value
in r-value contexts, and on the object and property in l-value contexts.


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

Why should that work? The valueOf function isn't guaranteed to return
anything related to the object it's on.


    (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.

This is exactly correct. Extracting a method from its object will break the
connection to the object.
Which is kindof expected when you allow any function to be used as both a
method and a non-method.


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?


When I think of References as l-values, the current behavior actually become the expected one. The only tricky bit is that method-calls actually need an l-value to
work correctly.

/L
--
Lasse Reichstein - reichsteinatw...@gmail.com
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to