Open Igmat opened 5 years ago
I think class-fields
is a great place for the proposal, after all, many people dislike the proposal, and what better place to suggest alternatives than there? If the idea turns out to be good, then it would be even fine to update that repo if not creating a new repo.
Placing it here may not get the idea as much exposure unfortunately.
@trusktr make a new repo for your own proposal; it's up to the champions what they want to entertain in a proposal's repo. If the idea turns out to be good, it is up to the champions to incorporate it into their proposal, or, it's up to the committee to advance the new proposal.
I revisited a little bit my approach, and found out that there are no major reasons to not make it more class-fileds
compatible, so to adjust initial proposal a little bit we can change this rule:
Proper assignment always has receiver,
[]
and has no keyword.
To "Proper assignment always has receiver and has no keyword." This leads to following additions (but not replacement, everything shown above is still valid syntax):
public #x;
obj.#x = 1;
public #x;
existingObj.#y = 1; // throws because #y isn't declared yet
const obj = {
#x: 1,
public #y: 1,
private #z: 1,
};
const otherObj = {
private #x: 1, // shadows `#x` from outer lexical scope
#y: 1, // throws because #y isn't declared yet in this scope
private #z: 1,
};
public #x;
existingObj.#y = 1; // throws because #y isn't declared yet
class SomeClass {
#x = 1;
public #y = 1;
private #z = 1;
};
class SomeOtherClass = {
private #x = 1; // shadowed `#x` not shared with `SomeClass`
public #y = 1; // another `#y` not shared with `SomeClass`
private #z = 1; // another `#z` not shared with `SomeClass`
};
So the only major difference comparing to your existing proposal @jridgewell, is need for the keyword. Does it make sense to you?
I've just got an idea how to add private #x for 'key'
, what would it mean and how to add reasonable protected
that actually will protect
something. @ljharb, I remember that you had concerns about this keyword. Does it make sense to describe it, @jridgewell, @ljharb? Or am I going too far with this shorthand syntax proposal?
So, I've already thought of almost exactly this, but I was limiting it to private
:
private #x; // #x is a constant binding.
obj.#x // dot access
Except, I was going to allow a PrimaryExpression
(bare) form:
obj[#x] // Same as `obj.#x`
// Share the #x reified private symbol
someFunction(#x) // #x is just CallExpression argument,
// providing the reified private symbol
This was to support dynamic access usecases.
@bakkot mentioned to me in the last meeting a desire to make all private declarations include the private
keyword:
class Ex {
private #x = 1;
}
This was specifically to allow sharing an outer scoped private in inner scopes, and to support top-level private eventually:
private #x;
private #y;
class Ex {
#x = 1; // Reuses outer #x
private #y; // Declares new #y
}
Overall, I like this idea. But, it needs to be discussed in the context of class-fields, not just private symbols. I won't be adding this to the private symbols proposal unless class-fields adopts it first. Adding more changes compared to the class-fields proposal gives the committee more things to complain about, and I'm already fighting an uphill battle.
I'm a little hesitant to add #
support for public symbols. Having this.#x
always mean "private x" makes code easier to reason about. But I'm not against the idea entirely.
Provided the committee decides to go with private symbols, I'd be happy to work on this as a follow-on proposal to see what the committee thinks.
Protected (as it behaves in other languages) just isn't going to happen in JS. The only way to do it is adding access-guard checks to all property lookups, which the implementers are going to reject outright.
But, what most people want with protected is just a shared private symbol. I think this is the path forward, not a protected
keyword.
@jridgewell, you can't even imagine how happy am I to hear that you're working in this direction. If you need any help (docs, readmes, faq, transpilers, tests or whatever else), feel free to contact me (here or directly ichulinda@gmail.com / ichulinda@dataart.com) - I'll be glad to help you.
Except, I was going to allow a
PrimaryExpression
(bare) form:
I didn't mention it explicitly, but my proposal includes #x
bare form for reified key.
I'm a little hesitant to add
#
support for public symbols.
Ok, I understand that. We may exclude it for now.
Protected (as it behaves in other languages) just isn't going to happen in JS.
I didn't even thought that it has to provide exactly same way as in other languages. My idea is related to scope, especially for for('x')
symbol API. Briefly, it should look like:
public for
- global scope (as it works now)protected for
- module scopeprivate for
- lexical or closure scopeBut let's keep it aside - it indeed could be follow-up, that we may dicuss later.
Overall, I like this idea. But, it needs to be discussed in the context of class-fields, not just private symbols. I won't be adding this to the private symbols proposal unless class-fields adopts it first.
Ok, I understand that, but taking @littledan's feedback in https://github.com/tc39/proposal-class-fields/issues/206#issuecomment-452382774 and https://github.com/tc39/proposal-class-fields/issues/183#issuecomment-452425068 into account it seems that there are no proper place to discuss something that relates to both class-fields
and private symbol
.
Obviously, you as committee members may have some other communication channels, but it would be great if we (I mean community) have an option to follow what is going on on this field, at least in read-only format. @littledan, @jridgewell what do you think about that?
From my side, I'm very interested in Symbol.private
evolving and would love to help. For instance I may describe new syntax (for private
only) in more details, and create separate documents for possible follow-ups (public
, protected
and for
) with high-level overview. What would you prefer from the following list?
Also, since I don't see how the process works inside, my assumptions about which activities are most valuable could be wrong, so feel free to point the correct direction, which will most likely lead to Symbol.private
acceptance.
I don't know for sure, but I guess that @shannon, @rdking and @hax could be interested in this too.
I didn't mention it explicitly, but my proposal includes #x bare form for reified key.
Sweet. I think I got confused when you said "proper assignment always has receiver, []
and has no keyword", but now I realize you were specifically talking about object assignment.
taking @littledan's feedback into account it seems that there are no proper place to discuss something that relates to both class-fields and private symbol.
I read his comments as "I don't want to work on private symbols", not that he doesn't want to discuss things that affect them both. Changing #x = 1
to private #x = 1
in class-fields is a specific request that actually applies to the class-fields proposal.
I know the committee members have privately discussed it, but I don't see it anywhere on the class-fields proposal. There was a strawman proposal to use an outer
keyword:
class Outside {
#x = 1;
#y = 2;
constructor() {
class Ex {
outer #x = 1; // Reuses outer #x
#y = 3; // Declares new #2
}
}
}
I like using private
keyword to declare a new lexical private more than the strawman's outer
(@bakkot does, too, this is what we talked about). We should bring this up so there's an actual issue on it. Similar issues: https://github.com/tc39/proposal-class-fields/issues/71, https://github.com/tc39/proposal-class-fields/issues/111, https://github.com/tc39/proposal-class-fields/issues/126
What would you prefer from the following list?
Separate repos please. Or, it can be your own fork of this repo. I just don't want to confuse people who visit this repo, I intend to keep this one as minimal as possible so it's easier to get through committee.
@jridgewell
But, what most people want with protected is just a shared private symbol. I think this is the path forward, not a protected keyword.
I'd wager what most people want is both. Familiarity with the meaning of existing keywords helps speed along understanding. So a declaration like protected #foo = 2
would be pretty clear to most that the symbol #foo
would be available in a derived class after they get used to # === Symbol
.
Originally I posted it in https://github.com/tc39/proposal-class-fields/issues/206, but it seems that
class-fields
were improper place for such proposals. @jridgewell, I've checked your presentation and it seems that following proposal could be used to adjust yourSymbol.private
approach. What do you think about that?Syntax
Declaration
Use
public
,private
(protected
as follow-up) as declaration keyword, likevar
/let
/const
.Assigment
Any assignment without receiver leads to early SyntaxError
Proper assignment always has receiver,
[]
and has no keyword.Declaration + computed property syntax in object literals
The true power comes with computed property syntax.
Declaration + computed property syntax in classes
Work mostly the same way as for objects.
Simple mental model
#
stands forSymbol
. Any variable starting with this sign is ALWAYSSymbol
. So code like thisprivate #x
should be read asprivate symbol x
.Discussible moments
I used already reserved keywords, since we are safe to use them + they are good fit for such mental model. But, obviously, we could select some others, for example:
Possible follow-up proposals
Symbol.protected
/Symbol.friend
/Symbol.<whatever>
and<whatever> #x
declaration syntax;<whatever> #x for 'key'
as shorthand forconst x = Symbol.for('key')
;