Ticket #117 (new defect)

Opened 1 year ago

Last modified 9 months ago

'this function' not supported by parser, evaluator

Reported by: lth Assigned to: jeffdyer
Type: defect Priority: major
Milestone: M2 Component: RefImpl
Version: 4 Keywords:
Cc: dherman, graydon, jaz

Description

The "remove arguments object" proposal introduces "this function" as a new primary expression. It must be supported by the parser, definer, verifier, and evaluator.

Please do your parts and pass the bug on as appropriate

Attachments

Change History

  Changed 9 months ago by jaz

  • cc changed from dherman, graydon to dherman, graydon, jaz

Is there a more detailed description of this than what appears in the "remove arguments object" proposal?

Is "this function" a syntax error if it appears outside of a function, or does it evaluate to undefined in those cases?

Does "this function" evaluate to a class object if it occurs in a constructor?

Can "this function" be used to capture a getter or setter function?

follow-up: ↓ 3   Changed 9 months ago by lth

The description on that proposal page is the only one that exists, to my knowledge.

"this function" should be a syntax error in top-level code and in getter and setter functions.

We never talked about constructors when we discussed the feature. I like your suggestion about returning the class, so let's try that.

In normal methods it must return a bound method, naturally.

in reply to: ↑ 2 ; follow-up: ↓ 4   Changed 9 months ago by jaz

Replying to lth:

"this function" should be a syntax error in top-level code and in getter and setter functions.

Just curious: why not allow 'this function' in getter and setter functions?

We never talked about constructors when we discussed the feature. I like your suggestion about returning the class, so let's try that. In normal methods it must return a bound method, naturally.

Right.

Okay, currently. the RI only reifies bound methods that are defined on an object's most most specific class. It does not reify bound methods defined on an object's ancestor classes. This makes sense in the absence of 'this function':

class A { function foo(x) {} }
class B extends A { override function foo(x) super.foo(x) }
var b = new B();

I can refer to a bound instance of B's foo method by:

b.foo

However, in the absence of 'this function,' there is no way to refer to an instance of A's foo method bound to b. With 'this function,' however:

class A { function foo(...xs) [this function].concat(xs) }
class B extends A { override function foo() super.foo(this function) }
var b = new B()
var foos = b.foo();

Here, of course, foos[0] should be an instance of A's foo method, bound to b. Which means that:

var foos2 = b.foo();
foos[0] === foos2[0] // => true

The RI's current treatment of superclass methods makes that identity difficult to get right. A bound instance of B's foo method will be stored in b's props table as a Mach.OBJ. But when b.foo calls A's foo, the raw function is looked up and invoked; it's never reified as a full value, and if it were, there wouldn't be anywhere to put that value.

So. any suggestions on how to handle this in the RI? We could expand Mach.PROP_BINDINGS, but that strikes me as potentially painful.

in reply to: ↑ 3 ; follow-up: ↓ 5   Changed 9 months ago by lth

Replying to jaz:

Replying to lth:

"this function" should be a syntax error in top-level code and in getter and setter functions.

Just curious: why not allow 'this function' in getter and setter functions?

Getter and setter methods are not functions that can be referenced from outside the class (you can't say obj.p to get the getter for p). I think the same ought to be true inside the class for the sake of logical consistency.

We never talked about constructors when we discussed the feature. I like your suggestion about returning the class, so let's try that. In normal methods it must return a bound method, naturally.

Right. Okay, currently. the RI only reifies bound methods that are defined on an object's most most specific class. It does not reify bound methods defined on an object's ancestor classes. This makes sense in the absence of 'this function': {{{ class A { function foo(x) {} } class B extends A { override function foo(x) super.foo(x) } var b = new B(); }}} I can refer to a bound instance of B's foo method by: {{{ b.foo }}} However, in the absence of 'this function,' there is no way to refer to an instance of A's foo method bound to b. With 'this function,' however: {{{ class A { function foo(...xs) [this function].concat(xs) } class B extends A { override function foo() super.foo(this function) } var b = new B() var foos = b.foo(); }}} Here, of course, foos[0] should be an instance of A's foo method, bound to b.

I think that's right.

Which means that: {{{ var foos2 = b.foo(); foos[0] === foos2[0] // => true }}}

I don't think that's required (or that it follows). There's nothing in the proposal about memoizing bound methods, and I don't think there should be. Most high-performance implementations will never bind methods until they are extracted (methods will not close over the this object), so it would be strange if they couldn't just cons up a new closure whenever the method is extracted.

The RI's current treatment of superclass methods makes that identity difficult to get right. A bound instance of B's foo method will be stored in b's props table as a Mach.OBJ. But when b.foo calls A's foo, the raw function is looked up and invoked; it's never reified as a full value, and if it were, there wouldn't be anywhere to put that value. So. any suggestions on how to handle this in the RI? We could expand Mach.PROP_BINDINGS, but that strikes me as potentially painful.

No suggestions from me this week, I haven't looked at Mach in a long time. But maybe the observation above (about the non-requirement for === to return true) can be of use to you.

--lars

in reply to: ↑ 4   Changed 9 months ago by jaz

Replying to lth:

Getter and setter methods are not functions that can be referenced from outside the class (you can't say obj.p to get the getter for p). I think the same ought to be true inside the class for the sake of logical consistency.

I don't see how this is an "inside the class/outside the class" issue. Classes do not appear to have anything to do with it:

var o = {get x() this function}

In the absence of 'this function,' getters and setters can't be accessed, at all. But then, neither can bound methods defined on an object's superclass (as discussed below). These strike me as very similar issues.

That said, I'm not arguing that we need 'this function' in getters/setters. I just find allowing it to be more consistent with the rest of the design, whereas you find it less consistent.

Which means that: {{{ var foos2 = b.foo(); foos[0] === foos2[0] // => true }}}

I don't think that's required (or that it follows). There's nothing in the proposal about memoizing bound methods, and I don't think there should be. Most high-performance implementations will never bind methods until they are extracted (methods will not close over the this object), so it would be strange if they couldn't just cons up a new closure whenever the method is extracted.

Fine with me. I had noticed that the RI does preserve identity in this case:

class A { function foo(x) x}
var a = new A();
a.foo === a.foo // => true

But if this isn't required by the spec, then there's no problem.

  Changed 9 months ago by jaz

Implemented in 0f0853ce9e982b5334d3528044232f8fe57fbf42.

Details:

- in a constructor, 'this function' evaluates to the class object

- a syntax error is raised (in the defn stage) if 'this function' appears

  • at the top level
  • in a getter/setter
  • in a class's settings

(I'm not sure about the last one. It could be treated as if it appeared in the constructor proper.)

- bound methods defined on an object's ancestor classes are not memoized (although ones defined on the object's own class are)

Example:

(function(n) (n === 0 ? 1 : n * this function(n - 1)))(5) // => 120
Note: See TracTickets for help on using tickets.