Orthogonal-Classes icon indicating copy to clipboard operation
Orthogonal-Classes copied to clipboard

Prototype Placement of Private Methods.

Open allenwb opened this issue 8 years ago • 10 comments

Private methods on the prototype are pretty useless as there is no convenient way to reference them. Consider:

class X {
   #helper() {};  //private method on prototye
   leader() {
          #this.helper(); //error because instances don't have a #helper field
          #helper() ;   //error because it means same as  this.#helper() 
          this.__proto__.#helper();  //error if invoked on a subclass instance
          X.prototype.#helper();    // works (assuming no rewiring) but ugly
                  //better to use a static private method: 
                  //static #helper() {}; 
                  // X.#helper();
  }
}

Class declaration scoped function declarations are probably more convenient and useful than either prototype private methods or static private methods. Eg:

class X {
  function helper() {);
  leader() {
     helper();
 }
 static supremeLeader() {
    helper()
  }
}

For this reason, I think we should excluded prototype placement of private methods from this proposal and possibly push for the inclusion of class scoped function declarations.

allenwb avatar Jan 30 '17 14:01 allenwb

Agreed on the immediate point.

On the other point, I find it attractive in the abstract to have class-scoped declarations. But I am at a loss for a good syntax. Whatever the syntax is, it should treat all declarations the same way. Making a special case for function declarations seems bizarre.

erights avatar Feb 08 '17 03:02 erights

It seems to me that we already have a perfectly fine syntax for class scoped declarations -- it is exactly function, 'let, const, class (and unfortunately var) declarations. I believe this with even discussed within TC39 as one of the motivations for using a StatementList-like (semi-colon separated) list for class bodies.

class X {
  function helper() {);
  const mask=0x00ff00ff;
  leader() {
     helper();
 }
 let [x,y]=aPoint;
 static supremeLeader() {
    helper()
  }
  class inner {}
}

allenwb avatar Feb 08 '17 03:02 allenwb

Awesome! Yes, we should push for this. But separately from this proposal, as you way.

Your example's indentation is weird.

erights avatar Feb 08 '17 04:02 erights

What if we said that the semantics of a private method which is not 'own' is that it would be in an immutable private slot, and an 'own' private method would be in a mutable private slot? This would make the values of the immutable methods into a sort of 'fake prototype', not reified at all (they would still be instance slots), but similar to prototype methods in that their value is shared by all instances.

littledan avatar Feb 13 '17 16:02 littledan

In my early work on "private" methods, I desperately wanted class-scoped function declarations, but there are some parsing issues.

Consider this token stream:

class X {
  async function [rest of stream]

When you read the async, you need two more tokens of lookahead to distinguish between an async method and an async function declaration.

zenparsing avatar Mar 02 '17 17:03 zenparsing

Any ambiguities in addition to async function/method?

While it's unfortunate to have such a special case, the easy solution would be to simply not allow async function declarations within a class definition. Assuming that we do allow let declarations within a class, a class scoped async function could still be created via let f = async function() ....

Another approach, that unfortunately we are probably too late for, would be to forbid (via a look ahead restriction) the use of the identifier function as the PropertyName within an AsyncMethod. An AsyncMethod named function could still be defined by saying async "function" () ....

allenwb avatar Mar 02 '17 18:03 allenwb

see https://github.com/tc39/ecma262/issues/832

allenwb avatar Mar 02 '17 21:03 allenwb

@bakkot Is this lookahead worse than what we need for the currently proposed property syntax? IIRC you ran into some pretty hairy cases involving somewhat deep lookahead, but were able to implement it reasonably in the V8 parser. I don't think the syntax has changed since you did that work, either.

littledan avatar Mar 02 '17 22:03 littledan

@littledan

The current proposal already requires two tokens of lookahead to distinguish between a static getter and a static field named get:

class A {
  static get p() {}
}

vs

class A {
  static get = 0;
}

Assuming we don't have static class-scoped declarations (as below), which I can't imagine why we would, I don't think this makes things any worse.

class A {
  static async function f(){}
}

vs

class A {
  static async function(){}
}

bakkot avatar Mar 02 '17 22:03 bakkot

It's been a while since I've done any parser work, but I don't think the static member parsing requires the additional lookahead: you read off the static keyword and then parse the rest of it as a non-static class element. (MethodDefinition in the current ES language.)

Whereas for the async function ... case, you can't read off the async until you know if it's a function declaration or not. I mean, you could, of course, do all manner of crazy stuff with an ad-hoc parser, but I'm not sure if it would align with TC39's previously stated goals for the formal grammar.

zenparsing avatar Mar 02 '17 23:03 zenparsing