tc39 / proposal-class-fields

Orthogonally-informed combination of public and private fields proposals
https://arai-a.github.io/ecma262-compare/?pr=1668
1.72k stars 113 forks source link

Proposal: keyword to replace `#` sigil #56

Closed rattrayalex closed 6 years ago

rattrayalex commented 7 years ago

(I have edited this description from the original in response to feedback; original is here)

In https://github.com/tc39/proposal-private-fields, I see a redirect to this repo for future discussion and current status, as well as this:

Q: Can we reconsider, in the syntax, the decision to do... A: Yes, it's not too late.

There seems to be general dissatisfaction with the # sigil for private fields. I would like to propose an alternate syntax with the same semantics:

Use a private unary prefix keyword to access a "private this", such that private this.foo is identical to the currently proposed #foo.

class Point {

    private x;
    private y;

    constructor(x = 0, y = 0) {
        private this.x = +x;
        private this.y = +y;
    }

    get x() { return private this.x }
    set x(value) { private this.x = +value }

    get y() { return private this.y }
    set y(value) { private this.y = +value }

    equals(p) { return private this.x === private p.x && y === private p.y }

    toString() { return `Point<${ private this.x },${ private this.y }>` }

}

Other possibilities for the keyword include:

Advantages:

Downsides:

(original proposal was private.foo instead of private this.foo)

ljharb commented 7 years ago

In your example, p.private.x would conflict with a public property named "private" on p, so that's a non-starter.

bakkot commented 7 years ago

I'll look at this a bit more soon, but a first note: p.private.x is already legal syntax, and I don't think we want to change its meaning.

rattrayalex commented 7 years ago

Ah, yes, that was a rather silly oversight.

In that case, it would probably need to be Object.getPrivateField(p, 'x') or similar, but I'm not sure that approach would work either.

loganfsmyth commented 7 years ago

The goal of this proposal is true privacy, so exposing them programmatically like that is tough, which is why the goal is for it to be syntactic. Also important because if there are subclasses and they each have private properties with the same name, programmatic access has no way to distinguish them.

rattrayalex commented 7 years ago

To be honest, I'm still a bit unclear as to why class instances should be able to access each others' private fields at all. The equals method in the Point seems like something that should read from a publicly-accessible getter on the "other". I assume I'm missing something, though.

rattrayalex commented 7 years ago

Ah, interesting, thanks for the reminder/explanation @loganfsmyth. I'm curious what p.#x would compile to in a Babel scenario; is there an example of that anywhere? (I haven't spent as long on this proposal as I like to).

EDIT: thinking through a bit more, I assume this simply isn't possible to transpile with exactly correct semantics...

loganfsmyth commented 7 years ago

Right now it'll likely be WeakMap values https://github.com/babel/babel/pull/6120/files#diff-1c2c94ad021e392a33a5501ebf552d1aR6

I'm still a bit unclear as to why class instances should be able to access each others' private fields at all.

It's generally the standard for private. https://github.com/tc39/proposal-private-fields/issues/90#issuecomment-307201358

The equals method in the Point seems like something that should read from a publicly-accessible getter on the "other".

It's not guaranteed that all data being compared is public, or maybe the internal representation of the value is different from the public API, or it would allow for simpler comparison logic. Maybe there is internal state that isn't important to the user, but is important to the functioning of the class.

rattrayalex commented 7 years ago

Cool, thanks for the diff, that's helpful. I haven't used private in other languages.

In any case, I assume p.private.foo is unresolvable, and it sounds like that feature is a hard requirement.

The only alternative I can think of is a prefix unary private keyword for access:

class Point {

    private x;
    private y;

    constructor(x = 0, y = 0) {
        private x = +x;
        private y = +y;
    }

    get x() { return private x }
    set x(value) { private x = +value }

    get y() { return private y }
    set y(value) { private y = +value }

    equals(p) { return private x === private p.x && y === private p.y }

    toString() { return `Point<${ private x },${ private y }>` }

}

I believe this could have similar precedence to typeof.

I find this ugly, and probably surprising coming from other languages, but possibly less so than # (obviously very subjective).

Thoughts?

bakkot commented 7 years ago

private p.x

I think that's technically feasible, although it gets quite awkward for nested access (what does private foo.bar.baz mean? is bar the private field, or baz, or indeed is foo a private field of this?) but I certainly find it more awkward and surprising than #.

# has a nice story, which is that it's part of the name of the field: where we might previously have written obj._foo to mean that _foo is intended to be private, now we can write obj.#foo to mean that #foo is actually private.

Also, one more note: private is legal as an identifier name in sloppy-mode code, which makes it a bit weird to give it keyword semantics in class bodies. It's viable, since class bodies are necessarily strict, just... weird.

rattrayalex commented 7 years ago

Hmm, private foo is more surprising than #foo?

I certainly agree the private other.foo is awkward, and parens may be needed in certain cases to distinguish (users may choose private(other.foo).bar as syntax).

Frankly, though, private seems like a feature that will be primarily written by a fairly small number of advanced javascript users (library authors) in a fairly small set of circumstances (class instance fields that must not be exposed). Indeed, I would expect these authors to be comfortable with the semantics of typeof, for example, and reading MDN docs in general.

In other words, I don't predict that private other.foo would be sufficiently confusing or awkward to unduly inhibit its use from the target audience as I understand it.

Most javascript users will probably only interact with the feature when reading library code to debug something, and private foo is more likely to be loosely intuitable than #foo.

As language features go, I would guess that private other.foo/other#foo is likely to be about as common as bitwise shifts or xors, which many JavaScript users (including some advanced users) are not aware exist at all.

On those grounds, I am hopeful that free sigils would be preserved for features that might be more commonly-used, and that some awkwardness with a rather niche, advanced feature would be tolerated.

Thoughts?

bakkot commented 7 years ago

Hmm, private foo is more surprising than #foo?

With the rest of the syntax you've proposed, much. Especially to users of languages like C++ and Java, for whom private foo as a declaration is very strongly tied to this.foo as access, which, as the FAQ points out, will not error but will just silently have the wrong semantics. (I don't think private.foo is enough to avoid that either.)

"Surprising" is less about the very first time you use it and more about all the rest of the times.

Frankly, though, private seems like a feature that will be primarily written by a fairly small number of advanced javascript users (library authors) in a fairly small set of circumstances (class instance fields that must not be exposed).

Does it? I dunno; I think those are the people for whom the feature will be necessary, but I expect plenty of people will use it. Any time they don't want other code mucking around with their internal state, basically.

In other words, I don't predict that private other.foo would be sufficiently confusing or awkward to unduly inhibit its use from the target audience as I understand it.

Mm... having bad syntax in the language is a cost even if most people rarely need it. "Powerusers would still use it" isn't sufficiently good justification for bad syntax.


Personally, I also don't expect there to be all that many new syntactic features in the language in the future, beyond some of the current proposals, so I am less concerned about reserving syntax for future features. (OK, full disclosure, not just "don't expect", but "intend to prevent unless there's extremely good reason"; there simply isn't that much syntax a language can tolerate.)

I think if you want the # to be used for something else, you'd be better served by arguing for that feature, and why that feature is as necessary and as dependent on a sigil as this one. And keep in mind that the likely outcome of successfully convincing the committee to deny # to private fields would probably be "the language has no private fields", not "private fields go in with awkward and surprising syntax"; that would be the burden to overcome.

rattrayalex commented 7 years ago

Hmm, interesting, thanks.

Judging from a few threads (eg https://github.com/tc39/proposal-private-fields/issues/14), I think there may be disagreement between the community and TC39 on whether #foo is "good syntax".

I'm coming from a biased perspective of finding the notion of private fields to be an antipattern in general; I would much rather JavaScript not have them, but if it must, a little inconvenience doesn't bother me 😄.

I would imagine that TC39 members feel differently; it seems they have fought hard, often against the community, for this feature.

Feature design takes time; I won't be ready to propose something by the time this feature is adopted. Others in the future may have better feature ideas than either you or I can imagine today.

In any case, I'll amend this issue's description to reflect the current state of the proposal, wait a few days to see if anyone else is interested in weighing in, and close it if there is no further support.

nika-begiashvili commented 7 years ago

Can anyone explain why PHP manages to do it with private keyword and JS can't ?

claytongulick commented 6 years ago

@rattrayalex If you take a look at this discussion, you can see quite a bit of history and some extensively (exhaustively?) discussed proposals. I participated quite a bit, and somewhat vigorously agree with your distaste for the # syntax. I have to say though, due to the patience and excellent technical points of @bakkot and @littledan I've started to come around. Maybe spend a little time reading through that discussion and see if you come out of the other side of it with the same opinion? I'd really prefer that we separate privates from the class field proposal to wait until we get consensus and a more generic approach to access modifiers overall, but like I said, as I've spend more time dwelling on it, I'm starting to really see why # is the only feasible way to deal with privates. To @bakkot and @littledan 's point as well - I'm authoring a framework and app designer based around drag and drop web components, and I'm really wishing for a hard private sigil of some kind. this._please_dont_change_me isn't cutting it.

bokodi commented 6 years ago

How about this?

class Point {
    private x;
    private y;

    constructor(x = 0, y = 0) {
        this..x = x;
        this.['y'] = y;
    }

    equals(p) {
        return this..x === p..x && this..y === p..y;
    }

    toString() {
        return `Point<${ this..x },${ this..y }>`
    }
}

or this syntax

class Point {
    private x;
    private y;

    constructor(x = 0, y = 0) {
        this->x = x;
        this->['y'] = y;
    }

    equals(p) {
        return this->x === p->x && this->y === p->y;
    }

    toString() {
        return `Point<${ this->x },${ this->y }>`
    }
}
bakkot commented 6 years ago

@bokodi, please see the FAQ.

ikokostya commented 6 years ago

@bakkot But the FAQ doesn't provide any proofs that adding additional logic on access to property make execution slowly. It was already noticed in other discussion https://github.com/tc39/proposal-class-fields/issues/15#issuecomment-317155488

So, adding additional declaration for private field

class Foo {
    private x;
}

we can mark this property as private and check this flag at property access.

Also, JavaScript engines doesn't execute absolutely the same steps from specification. Therefore, without implementation details, we cannot talk about complexity and execution speed.

rattrayalex commented 6 years ago

@claytongulick @bakkot the rather slow coming-around seems like it may take rather a long time with the entire javascript community...

I certainly see why special syntax is needed at the point of access, and why a mere private foo declaration alone is not enough.

I'm not clear on why a sigil is needed instead of a keyword for this access (typeof seems like fine precedent here).

I also have to say, the this->myPrivateField syntax seems much nicer to me than #myprivateField... I haven't given a thorough read, though, so perhaps that would be unworkable.

rattrayalex commented 6 years ago

I have updated the description to request the syntax private this.field and private other.field, which I believe does not clash.

I would invite @bokodi to open a separate issue suggesting -> syntax.

rattrayalex commented 6 years ago

(glancing through a few other issues, I just want to commend the maintainers for the amount of negativity they've put up with – eek! you have my sympathy)

bakkot commented 6 years ago

@rattrayalex

the rather slow coming-around seems like it may take rather a long time with the entire javascript community

That's unfortunate, but it's the sort of thing we try not to let influence us too much. "The future is longer than the past" is the stock phrase here - that is, we try to care more about design that will be good for future programmers, rather than design that will make current programmers happy, since there are probably more future programmers than current programmers.

I'm not clear on why a sigil is needed instead of a keyword for this access

It's true that we could, in principle, have private [no LineTerminator here] IdentifierName mean this.#x. But there's a few problems with that - first, what would accessing that.#x look like; second, what would declaration look like? No answer to these has yet seemed adequate, nor does a novel one seem likely to. (And from a more stylistically perspective, it would be a shame if property access did not look like property access. private x just doesn't look like property access to me.)

I also have to say, the this->myPrivateField syntax seems much nicer to me than #myprivateField

That means something entirely different in C++, so is probably unacceptable on those grounds alone. But more generally, you still have to figure out what declaration would look like under that proposal - not private x, presumably. So some other new syntax? I have to say I don't entirely understand the perspective which finds that to be nicer than

class Foo {
  name;
  #secretId;
  constructor() {
     this.name = 'Bob';
     this.#secretId = Math.random();
  }
}

Anyway, other syntax proposals have been discussed at some length already, including the -> one and the one with a keyword for access. I don't know that I can address all of them in the FAQ, but certainly if you have suggestions for other things to add to the FAQ I'd be glad to hear them. In the mean time, I'm just going to have to point you to the existing lengthy discussions.

Re: your second comment: please take a look through the existing issues before opening new ones; these suggestions have all been discussed before.

rattrayalex commented 6 years ago

Sorry @bakkot , I updated my proposal while you were typing to recommend something you might like a little better.

Declare: private x; Access on this: private this.x Access on that: private that.x

Which I think is fairly clear / unambiguous – would you agree? It also makes property access look like property access, which I agree is a desirable property.

I won't comment on -> since that belongs in a separate thread, though I hope it's not snide to mention that #foo means very different things in other languages, from macros/pragmas to comments (I believe its meaning in JS would be fairly novel).

Apologies for rehashing discussions that have been had; I've tried to scroll through several of the conversations. They can be lengthy and haven't always seemed so productive 😄 (again, you have my sympathy).

bakkot commented 6 years ago

Which I think is fairly clear / unambiguous – would you agree? It also makes property access look like property access, which I agree is a desirable property.

Personally, no. I'd disagree pretty strongly with both, actually.

As the first question in the FAQ says, I think private x as a declaration where access is anything other than this.x / that.x is a no-go.

And I don't think private this.x looks like property access. Or rather, it looks like referring to the public field x and applying an operator to it, just as typeof this.x does. (This is especially problematic with nested property access, as I mentioned upthread when you proposed it earlier.)

They can be lengthy and haven't always seemed so productive

Well, yes. Like I say, if you have suggestions for things I can add to the FAQ to better answer these sorts of questions before they get brought up again, I'd be very interested!

rattrayalex commented 6 years ago

It's my impression that the syntax above I have is workable; if that's incorrect, I think I'll go ahead and close this issue.

If it's not unworkable (eg; clashes with existing syntax, or contains unresolvable ambiguities), I'd like to leave it open for a bit and then close if there's no appetite for this over #.

(EDIT: whoops, posted before reading your most recent comment again).

It seems this suggestion doesn't appeal to you, so I'll close it. Hopefully the great minds of the javascript community can come up with another alternative; I don't think anybody has claimed much fondness for #.

rattrayalex commented 6 years ago

Regarding the FAQ, I don't have any ideas; I think the feature as it stands is nuanced, and there does not exist a simple explanation of it.

To me, that implies that we have an overcomplex feature on our hands, which might give a language designer pause. I'm sure the champions of this feature are considering the tradeoffs carefully.

bakkot commented 6 years ago

The FAQ is mostly intended to address the reasoning behind this choice of syntax and semantics, rather than to describe them. The reasons behind the current proposal are indeed pretty nuanced, but most people shouldn't need to know them, and I think the explanation of how the proposal works is actually quite brief:

Use #x in a class body to declare a private field and obj.#x to access it (where previously you might have written _x and obj._x and just hoped your users would respect that); privacy is enforced by restricting the ability to refer to #x to the body of the class declaring it (that is, by lexical scope, the same way it is enforced for closures); private fields cannot be accessed dynamically; attempting to access a private field on an object which lacks it will throw a TypeError.

That's... pretty much it, modulo some details about proxies and evaluation order and so on, most of which apply to public fields too.

rattrayalex commented 6 years ago

Right, it's just that judging from existing threads, a lot of people are likely to ask "why" 😄

littledan commented 6 years ago

I agree with @rattrayalex that the syntax is unambiguous. If you would be happy writing things like this, then it would work just fine, in principle:

class C {
  private x;
  private y;
  constructor(x, y) {
    private this.x = x;
    private this.y = y;
  }
  print() { console.log(private this.x + private this.y) }
}

Where I'm skeptical of @rattrayalex 's proposal is if this is something people would prefer to write most of the time, rather than the one-character sigil. The most common suggestion I've heard is to just use the same syntax as TypeScript, which omits the private on uses.

There is a particular hazard to @rattrayalex 's proposal, which is that if a programmer omits the private keyword in the class body aside from declarations, using instead the TypeScript syntax, then everything just becomes public. To take the above example,

class C {
  private x;
  private y;
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  print() { console.log(this.x + this.y) }
}

The second version of the class would just work--pass all the tests, everything--with just the minor problem that x and y are ordinary public properties. I think this hazard is particularly severe because it matches exactly how some other systems currently denote private, but without any privacy at all.

rattrayalex commented 6 years ago

Reopening because I think @littledan raises some interesting thoughts, and I'm curious for broader discussion.

rattrayalex commented 6 years ago

The hazard you raise seems particularly scary.

Any linter would likely add a recommended rule to protect against this; when private x; is declared, accessing this.x without private should probably raise a linter error.

Come to think of it, I'm not sure that this footgun is absent from the status quo proposal:

class Foo {
  #x;
  bar() {
    this.x = 3; // oops!
  }
}

While I certainly agree it looks worse in the proposal I have made, even with # the above could easily occur if the author did not fully understand the # feature (eg; a quick glance at the docs could lead to somebody writing code like this), or because they simply missed a # when scanning a large block of code (ie; this.somePrivateField and this.#somePrivateField are visually similar).

As such, # should probably be used with a lint rule similar to the one I described above.

(Such a linter rule would need to handle cases where both private x and x are declared).

rattrayalex commented 6 years ago

Another approach might be to discourage declaration with private, or even disallow declaration without an initial value:

class Foo1 {
  private x; 
  constructor() {
    // Surprising that you need `private` here.
    private this.x = 4;
  }
}
class Foo2 {  
  constructor() {
    // Arguably this is less surprising with the declaration omitted.
    private this.x = 4;
  }
}
class Foo3 {  
  private x = 4;
  constructor() {
    // At least this would obviously fail while developing if you omitted the `private`.  
    const y = private this.x * 2;
  }
}
rattrayalex commented 6 years ago

Where I'm skeptical of @rattrayalex 's proposal is if this is something people would prefer to write most of the time, rather than the one-character sigil.

That's certainly a valid critique as well; the syntax I am proposing would be unarguably less ergonomic for authors.

I view this as a feature, not a bug; use of private fields should be discouraged amongst most application developers. As far as I understand it, this language feature is intended primarily for library authors who Know What They're Doing and have Read The Manual.

Furthermore, even experienced library authors are imposing a cost on their users every time they use private, because it hinders debuggability. This tradeoff is probably worthwhile in certain cases, but as the status-quo proposal stands, it will be more convenient to declare/use private fields than public ones: #foo vs this.foo.

This could encourage private fields as the default among application and library authors alike, which may harm the debuggability of the JavaScript ecosystem as fields which are safe to expose become locked-down.

hmm, this gives me another idea...

rattrayalex commented 6 years ago

One minor quibble:

The second version of the class would just work--pass all the tests, everything

I would hope that if indeed the privacy of the field were important, the author would have written a test for its privacy.

I would also add that this is not a very harmful error case; the code still works as expected.

A far more dangerous failure mode would be one like this:

class Foo {
  #somePrivateField = 7;
  increment() {
     return this.somePrivateField = this.#somePrivateField + 1; // Spot the typo!
  }
}

where the code does not work at all (and, in this rather contrived case, the private value is exposed to boot).

This class of bug seems more likely in a world with # than private because it's easy to miss a single character. The class you mention is one where the author probably either gets it all right or gets it all wrong, which I would argue is less problematic.

Either way, this feature is dangerous to use without a linter.

ljharb commented 6 years ago

Debuggability is unafffected, because actual debuggers will be able to show private fields. Absolutely the goal is to convince EVERY developer to make anything that can be private, private - not just library authors. More privacy is a good thing, always.

rattrayalex commented 6 years ago

Debuggability is unafffected, because actual debuggers will be able to show private fields.

Surely they will; however, many programmers use tools like console.log(someInstance.someField) for debugging. I know that some look down upon this form of debugging, but:

Thus while debuggability is certainly not reduced to zero, it is affected.

The same might be said of testability. Certainly well-written software that makes use of private fields is, if anything, more testable than code without private fields, but poorly-written software may be much less testable when private fields are used everywhere. Unfortunately, a large percentage of software is poorly-written (I assume @ljharb would agree with this).

More privacy is a good thing, always.

Hmm, is this a universally-held opinion amongst language designers, library authors, and other software engineers?

bakkot commented 6 years ago

@littledan

I agree with @rattrayalex that the syntax is unambiguous.

The ambiguity I meant to refer to was in private this.x.y. It could be made unambiguous by picking one interpretation or the other, but I'd worry.

@rattrayalex

it will be more convenient to declare/use private fields than public ones: #foo vs this.foo.

I'm not sure I understand this. The shorthand isn't in the proposal anymore, if that's what you mean, so declaration and use for private fields is exactly like that for public fields, except that private fields are prefixed with # (and can't be accessed dynamically). This symmetry is a pretty attractive part of the proposal as it stands, to me.

I would hope that if indeed the privacy of the field were important, the author would have written a test for its privacy.

I don't share this intuition. Certainly I am not in the habit of testing for the privacy of my private fields in my C++ classes, for example. (Also, I'm not sure how you'd go about testing it.)

I would also add that this is not a very harmful error case; the code still works as expected.

Strongly disagree. The code has the semantics you expect except that more things are part of your public API than you intended. That's a much subtler problem to notice, and a harder one to fix once it goes out in the world.

This class of bug seems more likely in a world with # than private because it's easy to miss a single character.

This is the sort of thing which is hard to discuss without some observation, unfortunately. For what it's worth, I don't share this intuition either, especially if you start treating the # as part of the name, as the current declaration syntax encourages. And "mistyping field names" is a problem people are already accustomed to, as opposed to "omitting a contextual keyword which modifies property access".

Either way, this feature is dangerous to use without a linter.

Hm. It doesn't feel any more dangerous than public fields - i.e., if you mistype a field name, you get the wrong thing, which is indeed a serious danger but already irrevocably part of the language; is there some other danger you mean?

rattrayalex commented 6 years ago

The shorthand isn't in the proposal anymore, if that's what you mean

Yes, that's what I meant; I wasn't sure if the shorthand had been removed. Thanks for clarifying; I rescind those comments.

Regarding writing a test for privacy, that's fair. I think you could write it like this:

it('does not allow public access to foo', () => {
  const p = new Point(1, 2);
  expect(p.x).not.toExist();
})

but I probably wouldn't write such tests myself either.

I would predict that this.foo/this.#foo typos will be common, but that's just a prediction. I agree that some observation would be needed to assess the danger of such typos, and I suggest that some such observation take place before making this feature an official part of JavaScript (eg; querying github usage).

wangzimei commented 6 years ago
j#, or #whatever

the situation
use # for private member
we don't like it

the thought
more than one thing under one name
prefix # to 2nd name, how about 3rd?

the proposal
prefix #1, not #, to 2nd name, i.e. this.#1x

the family
#v6js2017; // analog "use strict"
// your js2017 code here
#5f; // @f
#"111"#; // `111`
#3"111"; // `111`, copied from c++

the alternative
\ commonly used as another escape

😄

nika-begiashvili commented 6 years ago

what about other access modifiers ? are we going to use different symbol for each modifier or are we restricting any other access modifier at all ?

littledan commented 6 years ago

@nika-begiashvili Other access modifiers could be provided by decorators, as outlined in this sample code implementing several access modifiers.

ikokostya commented 6 years ago

@littledan Why speed access is matter for private modifiers, but for protected is not? Instead, private and protected modifiers can be checked at runtime without any sigil prefixes (as I mentioned before https://github.com/tc39/proposal-class-fields/issues/56#issuecomment-347692690)

aikeru commented 6 years ago

Pardon me, but I write JavaScript for a living and I don't find this.#foo disagreeable. I would be very sad to discover I needed to type all of private this.foo or similar to access private fields in JS, which don't behave the same as "private" in other languages and thus, shouldn't be assumed so.

If anyone is worried about typos with this.#x it only seems that much worse with private this.x as there are more cases in which more severe problems could be caused.

As an aside... It seems like everywhere I've seen that there are people who adamantly want a "private" keyword (as well as protected, readonly, abstract...) coincidentally tend to come from TypeScript, where private isn't really private in any sense, but the IDE helps make sure you treat it that way at design time. These type-system-related concepts do not translate well to a dynamic runtime where new code and conflicting definitions can be loaded on the fly, at any time, and you can't ensure that the "Foo" class defined in one context/window/etc. is the same "Foo" referred to in another. Transpilers like TypeScript have the huge benefit of...

JavaScript has neither of these guarantees/benefits, which is why it's not as simple as introducing a keyword, as much as that might be a desirable thing other languages can do. If it were easy, big corporations with genius talent wouldn't have been working on this since (at least) 1998 -- which they have been, on and off, at different times, including Microsoft, Adobe, Google, Netscape, etc.

This isn't the first time private was proposed/requested for JS. That happened like, 20 years ago.

Apologies for the length.

ikokostya commented 6 years ago

If anyone is worried about typos with this.#x

I think people worry about introduction new awkward syntax (like php's $) and about only private modifier support (yes, I read that protected modifier should be support via decorators, but I don't understand why). Programming language is created for people, not for machines. From this point of view private keyword is much more readable than some sigil before variable name. Of course, I suggest to write private modifier only once at declaration and check later in access at runtime.

Current proposal for private fields looks like satisfaction of JavaScript parser.

Unfortunately, all my questions (e.g. https://github.com/tc39/proposal-class-fields/issues/56#issuecomment-351120108) are ignored by @littledan.

Edit: This is also introduce inconsistency in language, because module public entities are exported using export keyword, but for class private members # is used. For example, in Dart all identifiers started with an underscore _ are private in library.

aikeru commented 6 years ago

@ikokostya I don't think your assertion that your questions are being ignored is at all fair. Your question has been answered more than once in multiple places. Perhaps you are not looking as hard as you could.

While it may be popular to say private is more readable, it's still subjective (ie: I say it's actually less so, because # variables are lexically bound, and not this bound), but we can put that aside for a second.

Your point about programming languages being created for people / not machines is a valid principle. Unfortunately, at the end of the day, a programming language whose meaning is in any way ambiguous to a machine or cannot be properly understood by a machine, is not a useful programming language. So, unfortunately, we're limited by what we can add to every existing JavaScript parser out there without making things too complex or breaking any legacy code, as well as targetting an entirely dynamic environment. Put simply, new syntax must play ball with parsing as it already exists today. Doing otherwise will break the web, which we cannot do and there is precedent for this.

I do not understand at all how or why you say that this introduces inconsistency with respect to the export keyword, except that I can tell you that the import/export keywords have very strict limitations placed on them in order to work as they do. For example, you cannot dynamically at runtime create new exports or imports using the keywords, ie: you can't eval("export default foo"). It's apples and oranges.

Dart, like TypeScript, again, gets to break from JavaScript. Dart doesn't need to follow the syntax rules or legacy constraints JavaScript is subject to, so it's rather irrelevant what it does compared to what JavaScript may be able to do because it has the same benefits JavaScript does not have.

You can disagree on the principles of not breaking the web or staying compatible or requiring all new JavaScript modules written with the syntax you want to be precompiled ahead of shipping to the browser, but these aren't likely to be productive because it's been tried before multiple times. You may just not be aware of attempts like StrongScript/SoundScript from Google or JScript.NET from Microsoft, for example. That these essentially do not exist today / were dropped should tell you something.

The TC39 github discussions in my experience have been nothing but open, taking all comers of any experience level.

KnightYoshi commented 6 years ago

While it may be popular to say private is more readable, it's still subjective (ie: I say it's actually less so, because # variables are lexically bound, and not this bound), but we can put that aside for a second.

I don't see why a property defined with the private keyword couldn't be lexically bound instead of using the octothorp.

I saw a statement that people currently use underscore to denote when something is private, thus that is one reason why the ocothorp was chosen. However, the underscore is notation for readability. Even in PHP when defining private properties I've seen the underscore be used private $_prop1. It's just notation, not a language construct. If you don't use the private keyword for what its intended purpose, creating private members of an object, then what's the point of having it reserved. It'll never be used.

aikeru commented 6 years ago

@Knight-Yoshi this has been answered already ... but I think it can be summed up to this...

//within a class...
constructor() {
  this.privateVar = 5 //Does this refer to a real private variable, or a public one?
}
KnightYoshi commented 6 years ago

You should not be able to redeclare a property. In PHP this is valid

<?php
class Test {
    private $name = 'Me First';

    public function __construct() {
        $this->name = 'Me Second'; // refers to the private $name variable
    }

    public function name() {
        return $this->name;
    }
}

$t = new Test;
echo $t->name();

Inside the class name references the private variable.

aikeru commented 6 years ago

@Knight-Yoshi you can't compare php and JavaScript. They aren't bound by the same rules. What would you expect the behavior of this to be?

class Foo {
  private bar = 5
  constructor() { /* ... */ }
  doWork() { console.log(this.bar) }
}
var f = new Foo()
f.bar = 7
f.doWork()
KnightYoshi commented 6 years ago

I would expect an exception/fatal error be thrown for trying to access a private property.

aikeru commented 6 years ago

@Knight-Yoshi that doesn't work, because one of the explicit goals of the proposal is that private variables are undetectable from outside of the object. By wrapping a try/catch around that, I can detect that the private variable foo exists. Aside that, I'm also guessing that would also lead to a lot of surprising/undesirable behavior. #bar is necessary because it is literally impossible to collide with a public var / undetectable, because it is otherwise an invalid identifier.