Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why is the # needed? #7

Open
vanwagonet opened this issue Nov 18, 2015 · 22 comments
Open

Why is the # needed? #7

vanwagonet opened this issue Nov 18, 2015 · 22 comments

Comments

@vanwagonet
Copy link

What are the problems preventing the following syntax, and requiring the introduction of #?

class Privateer {
  private flag = 'GB';
  constructor (flag) {
    this.flag = flag
  }
  getCurrentAllegiance () {
    return this.flag
  }
}
@zenparsing
Copy link

See this tc39/proposal-private-fields#14 for an explanation of the issues.

@vanwagonet
Copy link
Author

That makes sense. I still think that omitting the this in favor of a plain #flag is inconsistent with the rest of the language as it currently stands. The private lookup is a (private) member lookup, not a plain variable reference, and would preferably always be part of a MemberExpression.

Perhaps this.#flag or this[#flag] would be less alien.

@trusktr
Copy link

trusktr commented Dec 10, 2015

I'm not so sure about having the # as part of the name either. It seems like

class Derived3 extends Base {
  // adds an additional private slot that hides inherited slot2
  private #slot2;

  getData1() {
    // returns undefined since subclass slot2 was not initialized
    return #slot2;
  }
}

could just be

class Derived3 extends Base {
  // adds an additional private slot that hides inherited slot2
  private slot2;

  getData1() {
    // returns undefined since subclass slot2 was not initialized
    return this.slot2;
  }
}

On the other hand, I do think it might be nice to replace the accessor operand (.) with another (#). For example:

class Derived3 extends Base {
  // adds an additional private slot that hides inherited slot2
  private slot2; // <-- clearly private

  getData1() {
    // returns undefined since subclass slot2 was not initialized
    return this#slot2; // <-- "#" instead of "." which denotes private access.
  }
}

so that when reading code it is clear that it's a private access, but the name of the variable isn't thought to include the #. But, then again, how do you say that? this.foo is this dot foo but how do you say this#foo? "this pound foo"? "this num foo"? I like the readability though. It's like replacing the common practice of this._foo with an official this#foo.

@trusktr
Copy link

trusktr commented Dec 10, 2015

And, what about for protected members? Maybe we can use a different symbol for that? Here are some with symbols that currently make SyntaxErrors: this@foo, this!foo, this~foo. There's some real-estate left.

Or, maybe we can use one or two of the same symbol to denote protected and private, respectively:

this#foo // protected
this##foo // private

this@foo // protected
this@@foo // private

this!foo // protected
this!!foo // private

this~foo // protected
this~~foo // private

We could also combine symbols:

this#foo // protected
this#>foo // private

But, I think my favorite is one of these:

this#foo // protected
this@foo // private

this#foo // protected
this~foo // private

this#foo // protected
this!foo // private

The exclamation kind of helps the notion of private, as in "watch out, this is sensitive data!".

None-the-less, I could live with a single symbol (this#foo) for both protected and private, but having two forms could be beneficial for readability. I could also just live with this.foo with errors thrown when I access something I'm not supposed to access, like in Java, where private, protected, and public members all blend together in terms of readability.

@trusktr
Copy link

trusktr commented Dec 10, 2015

For private methods, the same could apply:

class Foo {
  private foo
  protected bar
  constructor() {
    this!foo = 'foo'
    this#bar = 'bar'

    this#doSomething()
    this!doSomethingElse()
  }
  protected doSomething() { /* ... */ }
  private doSomethingElse() { /* ... */ }
}

@trusktr
Copy link

trusktr commented Dec 10, 2015

I forgot one symbol, the colon (:). Both : and ! have a period (.) in them, so they might play well together (.:!, they escalate):

this.foo
this:foo // protected
this!foo // private!

The only thing about the colon is that it might conflict with labels. But who uses those? Plus, it's easy to differentiate between a label for a loop and obj:foo.

class Foo {
  private foo
  protected bar
  constructor() {
    this!foo = 'foo'
    this:bar = 'bar'

    this:doSomething()
    this!doSomethingElse()
  }
  protected doSomething() { /* ... */ }
  private doSomethingElse(otherFoo) {
    console.log(otherFoo:bar)
    console.log(otherFoo!foo)
  }
}

@trusktr
Copy link

trusktr commented Dec 10, 2015

I may have missed why one of the above symbols can't be used. If so, just ignore that one.

@trusktr
Copy link

trusktr commented Dec 10, 2015

Oh, note that this.#foo would be one of those two-symbol combinations, and that I'd be fine with that but while using this.#foo but not simply #foo, as that seems more inline with the current language.

@trusktr
Copy link

trusktr commented Dec 10, 2015

Other possibilities:

this.foo
this.:foo // protected
this.!foo // private!

// or 

this.foo
this.#foo // protected
this.!foo // private!

while the declaration is still just

private foo
protected foo

without extra symbols (since the private and protected keywords are already there).

@trusktr
Copy link

trusktr commented Dec 10, 2015

I think, after all this, I kind of like that last idea most, the augmentation of this. by adding an extra symbol, so it can still read "this dot foo", but with a visual clue as to what type of access it is.

@zenparsing
Copy link

@trusktr So drop protected and just have this.@foo. Done! : )

@trusktr
Copy link

trusktr commented Dec 10, 2015

@zenparsing But, I do like the idea of protected members. I find myself wanting those often.

There is this package mozart which has protected members implemented just about as well as it can possibly be done in current JavaScript, but then we have to use the library everywhere in a code base if we want consistency, it has performance considerations, etc, so it has it's downsides.

@vanwagonet
Copy link
Author

@zenparsing this.@foo looks much much better than #foo imho. It's obviously a member expression. It's also obvious that the member is private, and so the lookup rules are different.

A .@ operator is also unambiguously different from the @ decorator proposal.

class Privateer {
  private flag = 'GB';
  constructor (flag) {
    this.@flag = flag;
  }
  getCurrentAllegiance () {
    return this.@flag;
  }
}

@PinkaminaDianePie
Copy link

``@` is not looked so good if es7 decorators (or any another annotation-liked syntax) will be also shipped in language:

class Privateer {
  @decotator private flag = 'GB';
  constructor (flag) {
    this.@flag = flag;
  }
  getCurrentAllegiance () {
    return this.@flag;
  }
}

two different functionalities but with similar characters in their syntax is not looked so good. maybe something C++ like ->?

class Privateer {
  @decotator private flag = 'GB';
  constructor (flag) {
    this->flag = flag;
  }
  getCurrentAllegiance () {
    return this->flag;
  }
}

@trusktr
Copy link

trusktr commented Dec 15, 2015

I thought of -> too, but I never liked it because the - is almost never aligned with the >, which bothers me. x]

@vanwagonet
Copy link
Author

I think the only one I really didn't like was a plain #flag with no this.

@carbonrobot
Copy link

Adding a random character to mark something private is unneccessary syntax. The compiler can treat it as such as well as any decent IDE.

I suggest marking it as private (or protected), and optionally accessing it with the this keyword.

class Derived3 extends Base {
  // private field
  private slot2 = 'something';

  getData1() {
    // optionally use the this keyword. slot2 is scoped to the class so its available here
    var k = this.slot2;
    var j = slot2;
    assert.areEqual(j, k);
  }

  getData2(slot2) {
    // slot2 is now scoped to this block, but this.slot2 is scoped to the class
    assert.areNotEqual(slot2, this.slot2);
  }
}

This is similar to the compiled languages

@zenparsing
Copy link

@carbonrobot

// What does this do?
Derived3.prototype.getData1.call({ slot2: "not-private" });

Without type information which can tell us whether a property name is private or public, we need a syntactically unambiguous way to make that determination.

@carbonrobot
Copy link

I would like to say that should be a compiler error, since using call in that way violates everything OOP about classes...but your right, its still supported.

@suchipi
Copy link

suchipi commented Apr 19, 2016

This proposal doesn't feel like it fits in with the language; the rules for access feel arbitrary and confusing. In addition, the # prefix feels unnecessary.

I'd prefer that private be semantically similar to a VariableDeclarator (ie. var, let, const), but the scope it defines its identifier as valid within is that of instances of the class (rather than the class itself):

class Foo {
  // The bar identifier is only accessible within this ClassBody,
  // but is unique to each instance of the class.
  private bar;

  constructor(val) {
    bar = val;
  }
}

bar; // ReferenceError

Foo.prototype.baz = function() {
  bar; // ReferenceError
};

I feel that this is a more natural fit for the language, and the rules around it are very simple to understand. If it is backed by private slots, that is fine by me, but that seems like an implementation detail (and therefore not worth introducing # for).

Additionally, static private could define class-level private members, but this is already pretty easy to accomplish with closures, so I'm not convinced it's entirely necessary.

I'm not sure how protected would fit into this model (if at all).

Also, it'd be great if private methods could be defined with the same semantics:

class Foo {
  private doSomeComplexTask(input) { /* ... */ }

  constructor(input) {
    doSomeComplexTask(input);
  }
}

(I actually find myself wanting private methods a lot more than private state, personally.)

@trusktr
Copy link

trusktr commented Apr 20, 2016

Omitting the this keyword removes the semantical importance of
this. Seeing bar = val; could make someone falsely believe that
bar is a previously declared variable, possibly from an outer scope;
there is added ambiguity. this.bar = val is much more clear. Having
some sort of symbol differing from the dot notation makes it even more
clear that it's a private access, f.e. this.-bar = val where -
means private. Your example would be

class Foo {
  private doSomeComplexTask(input) { /* ... */ }

  constructor(input) {
    this.-doSomeComplexTask(input);
  }
}

where the this.-doSomeComplexTask(input); is not ambiguous. At the
very least, I wouldn't mind if private properties (which can contain
methods) were used the same as current dot notation (f.e. this.bar),
where trying instance.bar from the outside would simply throw an
error. The extra symbol (f.e. this.-bar) would be a nice bonus.

On Mon, Apr 18, 2016 at 10:15 PM, Stephen Scott
notifications@github.com wrote:

This proposal doesn't feel like it fits in with the language; the rules for
access feel arbitrary and confusing. In addition, the # prefix feels
unnecessary.

I'd prefer that private be semantically similar to a VariableDeclarator (ie.
var, let, const), but the scope it defines its identifier as valid within is
that of instances of the class (rather than the class itself):

class Foo {
// The bar identifier is only accessible within this ClassBody,
// but is unique to each instance of the class.
private bar;

constructor(val) {
bar = val;
}
}

bar; // ReferenceError

Foo.prototype.baz = function() {
bar; // ReferenceError
};

I feel that this is a more natural fit for the language, and the rules
around it are very simple to understand. If it is backed by private slots,
that is fine by me, but that seems like an implementation detail (and
therefore not worth introducing # for).

Additionally, static private could define class-level private members, but
this is already pretty easy to accomplish with closures, so I'm not
convinced it's entirely necessary.

I'm not sure how protected would fit into this model (if at all).

Also, it'd be great if private methods could be defined with the same
semantics:

class Foo {
private doSomeComplexTask(input) { /* ... */ }

constructor(input) {
doSomeComplexTask(input);
}
}

(I actually find myself wanting private methods a lot more than private
state, personally.)


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub

@nijikokun
Copy link

The # symbol should not be a part of a private specification.

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

No branches or pull requests

7 participants