tc39 / proposal-private-methods

Private methods and getter/setters for ES6 classes
https://arai-a.github.io/ecma262-compare/?pr=1668
345 stars 37 forks source link

New, more JS-like syntax #20

Closed friendlyanon closed 6 years ago

friendlyanon commented 6 years ago

The proposed syntax for private fields is absolutely disgusting, to simply put, it is confusing, is without prior art and very much alien to the elegant syntax JS has.

I must ask, why did you decide to use such an ugly syntax? Last I checked private had no use at all. Combine that with the fact that we already in the process of adapting metaproperties, here is my proposal to you as to what an elegant syntax would look like:

class Counter extends HTMLElement {
  private xValue = 0;

  private get x() {
    return private.xValue; // !
  }
  private set x(value) {
    private.xValue = value; // !
    window.requestAnimationFrame(
      private.render.bind(this) // !
    );
  }

  private clicked() {
    private.x++; // !
  }

  constructor() {
    super();
    this.onclick =
      private.clicked.bind(this); // !
  }

  connectedCallback() {
    private.render(); // !
  }

  private render() {
    this.textContent =
      private.x.toString(); // !
  }
}
window.customElements.define('num-counter', Counter);

Reference: function.sent meta property new.target meta property

As much as I despise the idea of private fields, as I do believe this goes very much against the spirit of the open web and my love towards userscripts, at least make this abomination something pleasing to look at.

bakkot commented 6 years ago

This has been discussed at very great length. Please see e.g. the FAQ for private fields.

littledan commented 6 years ago

There are a couple of goals that this proposal is taking into account that inform the syntax:

It seems like the syntax proposal you made above doesn't quite meet these goals. I don't see a way to refer to other receivers, and the use of this seems implicit.

About aesthetics: I think people will get used to seeing # over time.

friendlyanon commented 6 years ago

@bakkot the part regarding why private was not used just says that other languages also use the keyword private for private fields and the access is done with this.x. I think we are far past the point where it should be dabated that despite JS looking like a mish mash of many languages, it very much is its own language, which I might add does not justify ugly syntax, however.

@littledan

But yes, personally my main issue is that very much alien syntax.

littledan commented 6 years ago

If the main issue is that the syntax is new, that's not an extremely strong argument. After a year or two, # may become normal-feeling.

rico345100 commented 6 years ago

This is way much better than #, but still I think private properties are not needed in JS.

SalvatorePreviti commented 6 years ago

+1

littledan commented 6 years ago

I believe that there should be an explicit distinction between accessing private and public fields. JS is moving in the direction of providing more explicit ways to express one's intentions.

There is a distinction. One has # and the other doesn't.

theKashey commented 6 years ago

Imo, accessing private methods not from this but from private – it quite good and elegant solution. It just clearly indicates - you cant access private methods of that, cos there is no this. In the same time - private must be bound to the same context, as... context, and...

this is already a source of enough confusion in JS; we'd prefer not to make it worse. Also, it's a major refactoring hazard: it would be surprising if this.x had different semantics from const thiz = this; thiz.x.

New context sensitive variable will just double happiness from the debugging.

amatiasq commented 6 years ago

The same way classes are sugar syntax for prototypes, the way @friendlyanon presents it:

class Test {
  private x = 0;

  private myMethod(value) {
    private.x = value;
  }

  myPublicMethod() {
    private.myMethod('test');
  }
}

It can be sugar syntax for:

const private = new WeakMap();

class Test {
  constructor() {
    private.set(this, {
      x: 0,

      myMethod(value) {
        private.get(this).x = value;
      }
    });
  }

  myPublicMethod() {
    private.get(this).myMethod.call(this, 'test');
  }
}

This will remove the collision issue and keep encapsulation.

friendlyanon commented 6 years ago

Funny thing is, that's a solution as old as WeakMaps themselves are. Excuse the wayback machine link, but here is an example https://web.archive.org/web/20140209122326/http://bbenvie.com/articles/2012-07-25/JavaScript-Classes-with-private-protected-and-super

baptistemanson commented 6 years ago

Should we say it "x"? "sharp x"? "hash x"? "private x"? How are we gonna communicate easily between us, human beings? Naming things is already hard. This proposal makes it even harder, because we have to think about accessibility at the same time as finding the correct name.

I put this link to Dijkstra point of view on identifiers if you are interested, which I think is aligned. https://www.cs.utexas.edu/users/EWD/transcriptions/EWD09xx/EWD958.html

TL;DR naming things is already not simple at all.

bschaepper commented 6 years ago

Private Fields and Methods are absolutely necessary I think, and I thank you guys for all your hard work in this and the private class fields proposal.

One problem with both solutions is, at least in my opinion, they make changing between public and private hard. I would happily change most class methods and fields to private, but changing every call and every reference is tedious.

I still do not follow the reasoning, why there is a need for a sigil at all. I mean I do understand the arguments, but don't think they are particularly convincing. I just re-read the FAQ @bakkot mentioned and it basically comes down to this:

Adding the prefix is the easy solution, while the right solution would be the Java-like Syntax everyone expects. This however is much dreaded, since property lookup is already complicated. Which might be a valid concern, but syntax matters a lot. Considering performance, I am quite sure, it is trivial for browser vendors optimize property lookup with runtime checks to be not noticeable. I assume accessing private fields will be faster than prototype properties, so fast adoption what something we want. Which is easier done with a nice syntax.

bakkot commented 6 years ago

@bschaepper, given the constrains, the committee feels the sigil is the right solution. We would not have chosen to go forward with it otherwise. The penalty of overloading this.x for property access isn't just in performance, but in complicating the mental model of property access even further: for example, this.x currently always goes up the prototype, but private field access by design does not.

As to changing between public and private, that's something I'd expect tooling to handle pretty well, just as it currently handles renaming fields.

bschaepper commented 6 years ago

That might be true for novices, but not for anybody who knows other OOP languages. To me, property lookup is perfectly logical and simple. You can always do stupid and confusing things, but otherwise things are great since classes and arrow functions, even for beginners.

Why we have to reinvent the wheel here is beyond me. Same goes for the whole "hard vs. soft private", and whether we want the Java equivalent of protected or private, or at all. There is one sane was to do it, and that is how the other major OOP languages do it, period. Just look how everybody assumed it would be done, look at TypeScript.

Anyways, I am very much looking forward to real privacy in JavaScript, even if I have to use ugly, hard to type # chars ;-)

bakkot commented 6 years ago

That might be true for novices, but not for anybody who knows other OOP languages.

I disagree, but also "this is confusing unless you also know a different language" doesn't seem that compelling to me.

There is one sane was to do it, and that is how the other major OOP languages do it, period.

Other major OOP languages have static type systems, as a rule. We have different constraints.

Just look how everybody assumed it would be done, look at TypeScript.

TypeScript's private fields aren't actually private, though.

littledan commented 6 years ago

As @bakkot and I have explained, these other syntax proposals just don't seem feasible, unfortunately.

Guxingzhe commented 6 years ago

Why not use this grammatical sugar: attributes or methods that begin with an underscore are privatized without the need to add the keywords "private" or "#". For example:

class Animal {
  _type = "human" // private attribute;
  age = 21; // public attribute;

  constructor(type, age) {
    this._type = type;
    this.age = age;
  }
  // private method
  _privateMethod() {
    console.info("this is my private method");
  }
  // public method
  toString() {
    this._privateMethod();
    return '(' + this._type + ', ' + this.age + ')';
  }
  // _type get set function
  get Type() {
    return _type;
  }
  set Type(type) {
    _type = type;
  }
}
let animal = new Animal();
animal.Type // yes
// _type cannot be accessed through this, age can;
animal._type // error: private attribute
animal.age // yes
// _privateMethod cannot be accessed through this, toString can;
animal._privateMethod() // error:
animal.toString() // yes

Don't you think it's more concise?

amatiasq commented 6 years ago

@Guxingzhe That was discussed before. Underscore has been used for a while to mean "this should be private, please don't touch it". But sadly we know this convention has been broken so many times, doing this modification to the language will mean tons of libraries and applications will break.

Remember: Don't break the web.

I'm not in favor of the current proposal but I understand there has been no better option yet.

Guxingzhe commented 6 years ago

@amatiasq Thank you for your answer, I only later thought of "undercode" and "loadash". It is acceptable to use "#" as "_";