{"action":"created","issue":{"url":"https://api.github.com/repos/tc39/proposal-decorators/issues/25","repository_url":"https://api.github.com/repos/tc39/proposal-decorators","labels_url":"https://api.github.com/repos/tc39/proposal-decorators/issues/25/labels{/name}","comments_url":"https://api.github.com/repos/tc39/proposal-decorators/issues/25/comments","events_url":"https://api.github.com/repos/tc39/proposal-decorators/issues/25/events","html_url":"https://github.com/tc39/proposal-decorators/issues/25","id":267014711,"number":25,"title":"Consider `protected`, `abstract`, and `friend`","user":{"login":"rbuckton","id":3902892,"avatar_url":"https://avatars2.githubusercontent.com/u/3902892?v=4","gravatar_id":"","url":"https://api.github.com/users/rbuckton","html_url":"https://github.com/rbuckton","followers_url":"https://api.github.com/users/rbuckton/followers","following_url":"https://api.github.com/users/rbuckton/following{/other_user}","gists_url":"https://api.github.com/users/rbuckton/gists{/gist_id}","starred_url":"https://api.github.com/users/rbuckton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rbuckton/subscriptions","organizations_url":"https://api.github.com/users/rbuckton/orgs","repos_url":"https://api.github.com/users/rbuckton/repos","events_url":"https://api.github.com/users/rbuckton/events{/privacy}","received_events_url":"https://api.github.com/users/rbuckton/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":19,"created_at":"2017-10-19T22:57:37Z","updated_at":"2017-11-11T11:44:52Z","closed_at":null,"author_association":"NONE","body":"While this is likely better suited for a separate proposal, I wanted to outline my early thoughts about accessibility modifiers, as a follow up to https://github.com/tc39/proposal-unified-class-features/issues/24#issuecomment-338051994. If this seems generally viable, I may work this up into a full proposal.\r\n\r\n# The `protected` modifier\r\n\r\nThe `protected` modifier allows a subclass to access all of the private names of a superclass that were declared as part of a `protected` member.\r\n\r\n## Example\r\n\r\n```js\r\nclass Super {\r\n  protected #x = 1;\r\n  protected #y() { return this.#x; }\r\n  constructor(x) { this.#x = x; }\r\n}\r\n\r\nclass Sub extends Super {\r\n  protected #y() {\r\n    // overrides #y in Super\r\n    return super.#y() + 1; // ok, since its the same #y\r\n  }\r\n  method() { return this.#y(); }\r\n}\r\n```\r\n\r\n## Suggested Semantics\r\n\r\n- To implement `protected` in a fashion that aligns with other languages, private named methods would need to be installed on the prototype and member lookups would require a prototype walk, as this is needed to support `super`. \r\n- Whenever a class extends a superclass, the private names of any `protected` method are added to the subclass instance. \r\n- A subclass may \"override\" a protected member of a superclass.\r\n- A subclass may not declare a new private member with the same name as a protected member of its superclass.\r\n\r\n# The `abstract` modifier\r\n\r\nAn `abstract` modifier could be applied to classes and both private and non-private methods.\r\n\r\n## Example\r\n\r\n```js\r\nabstract class Super {\r\n  protected abstract #x(a, b); // protected abstract\r\n  abstract y(c); // public abstract\r\n}\r\n\r\nclass Sub extends Super {\r\n  protected #x(a, b) {   }\r\n  y(c) { }\r\n}\r\n```\r\n\r\n## Suggested semantics\r\n\r\n- An `abstract` class has a specialized \\[\\[Construct]] method that throws if the current `new.target` is a constructor for a class that is `abstract`. \r\n- An `abstract` method must be declared in an `abstract` class.\r\n- An `abstract` method may have a parameter list, but its parameters may not have initializers and may be neither binding patterns nor a rest argument.\r\n- An `abstract` method may not have a body. When the method is defined during class declaration evaluation, it is given a body that throws a TypeError.\r\n- An `abstract` method with a private name must also be declared `protected`.\r\n- If a subclass is not `abstract`, but it directly extends an `abstract` superclass, it must provide an implementation for any `abstract` method.\r\n\r\n\r\n# The `friend` modifier\r\n\r\nThe `friend` modifier allows a class to indicate another class that can access its private and `protected` members. The befriended class can access those private names using a prefixed private name.\r\n\r\n## Example\r\n\r\n```js\r\nclass B {\r\n  getX(obj) {\r\n    return obj.A#x;\r\n  }\r\n  setX(obj, value) {\r\n    obj.A#x = value;\r\n  }\r\n  callY(obj) {\r\n    return obj.A#y();\r\n  }\r\n}\r\n\r\nclass A friend B {\r\n  #x;\r\n  protected #y() { return this.#x + 1; }\r\n  constructor(x) { this.#x = x; }\r\n}\r\n```\r\n\r\n## Suggested semantics\r\n\r\n- When declaring the friends of a class, the befriended class must already be declared and initialized.\r\n- The prefixed-private name has the form: _Identifier_ `#` _Identifier_. The lexical `this` of the caller is passed as a new argument to the private get operation and is used to check access.\r\n\r\n# Optional: The `private` modifier\r\n\r\nThe `private` modifier is only allowed on a private-named declaration and is optional. Generally it just exists to visually disambiguate between `private` and `protected` members."},"comment":{"url":"https://api.github.com/repos/tc39/proposal-decorators/issues/comments/343659066","html_url":"https://github.com/tc39/proposal-decorators/issues/25#issuecomment-343659066","issue_url":"https://api.github.com/repos/tc39/proposal-decorators/issues/25","id":343659066,"user":{"login":"littledan","id":189835,"avatar_url":"https://avatars3.githubusercontent.com/u/189835?v=4","gravatar_id":"","url":"https://api.github.com/users/littledan","html_url":"https://github.com/littledan","followers_url":"https://api.github.com/users/littledan/followers","following_url":"https://api.github.com/users/littledan/following{/other_user}","gists_url":"https://api.github.com/users/littledan/gists{/gist_id}","starred_url":"https://api.github.com/users/littledan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/littledan/subscriptions","organizations_url":"https://api.github.com/users/littledan/orgs","repos_url":"https://api.github.com/users/littledan/repos","events_url":"https://api.github.com/users/littledan/events{/privacy}","received_events_url":"https://api.github.com/users/littledan/received_events","type":"User","site_admin":false},"created_at":"2017-11-11T11:44:52Z","updated_at":"2017-11-11T11:44:52Z","author_association":"OWNER","body":"@rbuckton I'm still having trouble understanding the advantages of these modifiers being built-in as opposed to using decorators.\r\n\r\nIs this a categorical thing, that for something as core as access modifiers, it's inappropriate to put it in a library? Or that the syntax is nicer without an `@`, and friend element access is more ergonomic as `x.A#b` than `key.get(x, A, \"b\")`?\r\n\r\nOne thing that makes me a bit worried about adding access modifiers at this point is that they are complicated, detailed, and the proposal differs from what we have now (e.g., these are somewhat tighter than what TypeScript provides). A lot of the complexity is in `protected`, but many programmers have been giving the feedback that `friend` is more important.\r\n\r\n> The upshot of syntax is that it gives the engine the opportunity to establish and lock down member access in ways that may not be feasible in user code.\r\n\r\nYou mentioned stronger guarantees, but I don't quite understand. What wouldn't be feasible in user code? Is this about the distribution of the FriendKey?\r\n\r\n> The protected modifier allows a subclass to access all of the private names of a superclass that were declared as part of a protected member.\r\n\r\nProtected increases the dynamism of private names significantly. In particular, the lookup of an identifier `#x` becomes no longer lexical but instead there are other sources to consult (super-classes, friends) in a certain order.\r\n\r\n> One option is to define an intermediary:\r\n\r\nFor friends, a very common case is that class A is allowed to see the private elements of class B, and A is defined after B. For example, this comes up when class A is test code accessing the internals of class B. It would be pretty awkward to define an intermediate class AB as you're suggesting above, as opposed to using a `@testable` decorator which just exposes everything for metaprogramming.\r\n\r\n> Another option for friend would be to hold onto the binding of the friend (rather than the value) and only evaluate the binding when checking access:\r\n\r\nSeems like this only works if they're in the same scope. For example, if I have a class, and then tests in another file, I'm not sure how to use this mechanism."}}
{"actor":{"display_login":"littledan"}}