Orthogonal-Classes
Orthogonal-Classes copied to clipboard
Prototype Placement of Private Methods.
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.
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.
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 {}
}
Awesome! Yes, we should push for this. But separately from this proposal, as you way.
Your example's indentation is weird.
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.
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.
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" () ...
.
see https://github.com/tc39/ecma262/issues/832
@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
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(){}
}
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.