Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Use both the # sigil and keywords #61

Closed
lpd-au opened this issue Nov 23, 2017 · 17 comments
Closed

Use both the # sigil and keywords #61

lpd-au opened this issue Nov 23, 2017 · 17 comments

Comments

@lpd-au
Copy link

lpd-au commented Nov 23, 2017

For the record, I find the syntax of this proposal to be awful and hope it's rejected in its current form. However, considering this seems likely to be pushed through regardless of overwhelming feedback, I'd like to suggest an amendment to make it slightly more palatable.

class Example {
  private #field1;
  protected #field2;
  public field3, field4 = true, field5;

  private field6; // Error: private field names MUST begin with a hash
  protected field7; // Error: protected field names MUST begin with a hash
  public #field8; // Error: public field names MUST NOT begin with a hash

  constructor(field1, field2, field3, field5) {
    this.#field1 = field1;
    this.#field2 = field2;
    this.field3 = field3;
    this.field5 = field5;
  }

  ...
}

Basically: keep the hash to distinguish between public and non-public property names but use keywords before their declaration. Yes, this is slightly more verbose. However, the code's effect is significantly more clear to the reader. At worst, a newcomer might wonder why the author chose to name all their non-public variables with a leading hash, instead of currently mistaking it for a comment line. It's also alot more flexible; how does the current proposal allow for future extensions such as protected? This extensibility is imo a key advantage.

(Ideally, this principle would be applied but with an underscore instead of a hash. I've read the FAQ but the section regarding runtime checks on the receiver is completely unsubstantiated, and the section below that regarding lack of static types is nonsense [eg PHP manages just fine]. But I digress...)

@bakkot
Copy link
Contributor

bakkot commented Nov 23, 2017

See previous discussion, or this recent comment in a different repository for a summary.

I suppose it may be worth adding this to the FAQ too.

@lpd-au
Copy link
Author

lpd-au commented Nov 24, 2017

We did discuss making a (technically) redundant private keyword which would come before the # in declarations to make it clear that this is private. The conclusion was to omit this, based on a principle from @dherman , that we should be coding the syntax in a way that would be learnable and usable by practitioners, but that we shouldn't force the syntax to say something repetitive and meaningless just to force them to repeatedly document how the language works.

  1. I don't consider this keyword redundant unless you can explain how protected fields could later be implemented.
  2. Good syntax is not just learnable and usable, it's also readable. Are using the keywords any less learnable and usable? I'd argue they're even more so. Especially when using multiple declarations with commas, eg private #field1, #field2, the difference is a trivial 8 characters per class. That's not repetitive at all.
  3. Such widespread negative feedback is a sign that the proposal's principles are misprioritised anyway, especially when the feature would otherwise be much anticipated. The principle of least astonishment needs to be given greater emphasis here if you want real-world developers not to think you absolutely blew this opportunity.

@littledan
Copy link
Member

We should probably also reference the protected decorator story as well as the way that you can use ordinary symbols for protected in the FAQ. It has been explained on several bug threads.

@lpd-au
Copy link
Author

lpd-au commented Nov 27, 2017

@littledan you can also use ordinary symbols for private, so why do we need this proposal?

I hope this is not seriously your suggestion:

class C {
  #field1;
  @protected #field2;
  field3;
  ...
}

🤮

@ljharb
Copy link
Member

ljharb commented Nov 27, 2017

@lpd-au no, symbols are 100% public, because of Object.getOwnPropertySymbols. “private“ means it’s impossible to access it or even learn of it’s existence, by any means.

@littledan
Copy link
Member

@lpd-au yes, that's the idea. What's the problem? Too much punctuation?

@lpd-au
Copy link
Author

lpd-au commented Nov 28, 2017

@littledan

@lpd-au yes, that's the idea. What's the problem? Too much punctuation?

It's an absolute dog's breakfast of symbols and keywords, yes. There is zero consistency, it completely fails the least astonishment test and for you most importantly: no level of exposure/familiarity would ever make it appear as more than a bad hack.

Can you provide me with an example of the boilerplate code that would be necessary to make @protected work? Why is including that (plus the 11 characters per class for the decorator) okay, but 8 characters per class for a private keyword not okay?

@ljharb

@lpd-au no, symbols are 100% public, because of Object.getOwnPropertySymbols. “private“ means it’s impossible to access it or even learn of it’s existence, by any means.

If you want to be pedantic, sure, you have to use a weakmap for 100% privacy. But that means the point I was replying to, "that can use ordinary symbols for protected," is equally incorrect. My point is that there is no reason why protected should be a second class citizen. Just because it can be done in other ways, doesn't mean an inbuilt, cleaner and more user-friendly way shouldn't exist; we wouldn't have the class syntax at all otherwise.

@bakkot
Copy link
Contributor

bakkot commented Nov 28, 2017

@lpd-au, see previously and elsewhere on the topic of protected and other accessibility modifiers.

@littledan
Copy link
Member

@lpd-au is it boilerplate to import a library? We could later add this as a built-in contextual keyword if needed.

@lpd-au
Copy link
Author

lpd-au commented Nov 29, 2017

@littledan

@lpd-au is it boilerplate to import a library? We could later add this as a built-in contextual keyword if needed.

If it's to import something as fundamental as a reserved keyword, I'd probably actually have to argue yes, it is. You haven't answered either of my questions though.

@littledan
Copy link
Member

Please see friends.js in this repository for a possible library definition of protected.

As for why one is OK and not the other: there is a lot written about why the # is necessary and why strong privacy barriers are a goal. From there, private is a necessary default and more text is redundant. Protected does not offer any more privacy than symbols, which are already available.

@lpd-au
Copy link
Author

lpd-au commented Nov 30, 2017

There isn't one, but I assume you mean friend.js in the decorators repository, thanks.

@littledan

As for why one is OK and not the other: there is a lot written about why the # is necessary and why strong privacy barriers are a goal. From there, private is a necessary default and more text is redundant. Protected does not offer any more privacy than symbols, which are already available.

Your conclusions don't follow the premise:

  • Private is a necessary* feature, not a necessary default. You are choosing to make it a default over other viable options like that described in the OP.
  • Protected should/would offer more privacy than symbols by, as ljharb reminded us, stopping read and write via Object.getOwnPropertySymbols. One could use a weakmap to accomplish it, but so they could for private as well, with the same potential pitfalls in each case.

All of the clear double standards being applied here seem to boil down to this: if developers could only pick one, we'd opt for private over protected. That should not be used as justification to select a restrictive, unfriendly and ugly syntax.


I imagine the original purpose of decorators at the start applying to all following comma separated fields was precisely so they could implement hacks like @protected without repetition. (And now they're gone, hmm...) Another benefit of these "redundant" keywords is to alleviate that need and allow the typescript convention to be followed:

let a, b = 5, c;
var x, @gimmefive b, c;
private #a, #b = 5, #c;
public x, @gimmefive y, z;

With the keyword out the front, I see that confusion significantly diminished. At least it doesn't seem any more ambiguous than var a, b = 1.

@ljharb
Copy link
Member

ljharb commented Nov 30, 2017

@lpd-au "hard private" is both a necessary feature and default. "protected" would not provide any privacy because you could trivially observe it by making a subclass and using that to do so.

@lpd-au
Copy link
Author

lpd-au commented Nov 30, 2017

@ljharb

@lpd-au "hard private" is both a necessary feature and default. "protected" would not provide any privacy because you could trivially observe it by making a subclass and using that to do so.

Even if the concept of finality is never formally added to the syntax:

var C = (function () {
    class B {
        //protected #protectedField;

        constructor() {
            switch (this.constructor) {
                case B:
                case C: break;
                default: throw new Error("Stop looking at me!");
            }
        }
    };
    return class C extends B {};
})();

class Spy extends C {};
class SuperSpy extends (Object.getPrototypeOf(C.prototype).constructor) {};

new C(); // ok
new Spy(); // error
new SuperSpy(); // error

I'd say that's offering more privacy than symbols, but I'm happy to learn something and be proven wrong.

@ljharb
Copy link
Member

ljharb commented Nov 30, 2017

@lpd-au B/B.prototype is fully accessible from the prototype chain of C/C.prototype; you don’t have anything private there whatsoever (just like if you used Symbols).

@aaronbeall
Copy link

aaronbeall commented Dec 19, 2017

For the record, I find the syntax of this proposal to be awful and hope it's rejected in its current form.

With respect to all the thought and debate that led to this point, I completely agree. Reading the FAQ and comments it feels like the rationale for the magic syntax and against the keyword seems to be inconsistent, and shifts slightly depending on what part you're reading. Frankly I think we have yet to find an actually good solution, I hope this isn't over.

@littledan
Copy link
Member

Seems like we didn't really arrive at a change we want to make in this thread. We've been talking about protected extensively in various threads; I don't think we need this one as well. As we discussed in #83, not using a keyword here doesn't preclude us from adding keywords in the future. Closing this issue as I don't see any more actions to take from this thread.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants