-
Notifications
You must be signed in to change notification settings - Fork 113
Please do not use "#" #77
Comments
Please read the FAQ; you'll hopefully see why no keyword is feasible. As for your personal taste; "ugly" is subjective, and many don't find it ugly. |
Ok, subjectivity aside, # could have great potential for something in the future. Its a very assessable character. Right there above the middle finger. This is an edge case concern. I can appreciate the need to have truly private attributes. Why not ### instead of # given the far reaching concerns and utility a single # can offer. |
Consider, for example, that # in Scala is used for accessing nested classes. My point is that it is important to really consider the future of the language the ergonomics that are being forfeited. The FAQ is also full of opinions, for example, it is stated that I have to admit that having a This need for A keyword is plausible. It is clearer, easier to read, easier to teach, and better to look than a misplaced |
A keyword used for declaration that is not also used for access won’t work (per the FAQ). Do you have another single-character syntactic suggestion besides |
Imagine if Of course keywords are better. A keyword for generators would have been even better. What follows is actual Perl code. ($l=join("",<>))=~s/.*\n/index($`,$&)>=$[||print$&/ge; Let's stop making Javascript a write-only language. Adding these kinds of symbols is damaging to be sure. Perl went down this "developer ergonomics" rabbit hole. The aftermath of Perl attracted many to following wisdom https://www.python.org/dev/peps/pep-0020/
class Foo {
const private bar = 5
let private baz = 6
method() {
console.log( private bar)
private baz = 7
// private bar = 8 throws exception (const)
}
} What's wrong with that? |
It doesn’t allow you to access private values on other objects besides I’d really suggest rereading the FAQ; all of your reasoning is sound, but seems to be missing some of the constraints. |
I see this in the FAQ
Those words are there just hanging around, no examples. Please explain and amend the FAQ to state the issue clearly with code so that one can reason about what exactly is the problem. Thanks. |
Here's an incredibly necessary and common example: class Array {
#arrayData = [];
static isArray(obj) {
if (!obj) { return; }
try {
return !!obj.#arrayData;
} catch {
return false;
}
}
} Here's another: let i;
class Foo {
#id = i++;
compare(other) {
return other && this.#id === other.#id;
}
} |
function getFooClass () {
let internalParameter = 'bookkeeping'
let totalMembers = 0
let prototype
const Foo = function Foo() {
prototype.totalMembers = ++totalMembers
Object.defineProperty(this, 'totalMembers', {
get: function() { return totalMembers },
set: function() { throw 'read only' }
});
}
prototype = Foo.prototype
return Foo
}
const Foo = getFooClass()
const a = new Foo()
const b = new Foo()
a.totalMembers
// 2
b.totalMembers
// 2
Foo.prototype.totalMembers
// 2
a.totalMembers = 3
// read only Is this roughly what you are communicating? Please clarify. Note that function getFooClass () {
let internalParameter = 'bookkeeping'
let totalMembers = 0
let prototype
class Foo {
constructor(){
++totalMembers
Object.defineProperty(this, 'totalMembers', {
get: function() { return totalMembers },
set: function() { throw 'read only' }
});
}
}
prototype = Foo.prototype
return Foo
}
const Foo = getFooClass()
a = new Foo()
b = new Foo() ... or it can be made available to Foo but read only. Or not at all readable. Or we can return from getFooClass both the Foo class ( |
Your "totalMembers" example is what would be a static private; my examples use a static method to read instance private fields off of multiple objects. |
The above with the additional constraint that it would not be intended as an API... is that correct? I'm asking so I can clarify the problem to myself |
I'm not sure what you mean by "intended as an API"? |
OK, I better understand now, thanks--I'll think it over for a while because there needs to be something better than wasting Seriously if we are at a point where all other symbols are exhausted, extinguishing the last of the symbols from this endangered species is unwise. At least the https://github.com/tc39/proposal-pipeline-operator Its great and all breed of programmers benefit from it. From different skill levels to different philosophies. And that's two characters while far more useful for day-to-day programming. For now however, shouldn't there be a |
It’s a field, not a variable. |
Here you go const privateStuff = {}
const initPrivateStuff = function(instance) {
privateStuff[instance.id] = {
data: []
}
}
class Foo {
static isFoo(obj){
if(obj && obj.id){
return privateStuff[obj.id] != null ? true : false
} else {
return false
}
}
constructor(){
this.id = Symbol()
initPrivateStuff(this)
}
}
const foo = new Foo()
const bar = new Foo()
console.log(Foo.isFoo(bar))
//true
console.log(Foo.isFoo(foo))
//true If I understand the issue correctly, please consider if your objections are more valuable than forfeiting |
(To be clear: And yes, i think instance private is so massively important that even if it killed every future syntax proposal forever, it would be worth it. Just to be clear. (I personally find # here quite readable; there’s nothing like this in the language to maintain consistency with; i think it’s trivially teachable; etc) |
Disagree, but # would be a stupid syntax for that too.
Given the remark about the future, fervor here is so high as to be unreasonable and pertinaciously obstinate.
Strongly disagree. What problems does # solve here not easily address by my (possible more portable) example? That said it is valid to make the language more ergonomic with measure cost benefits. I personally feel JavaScript should grow its functional abilities. Its fine for classes to get better too, why not? People who don't like it can not use it. Done. But I a strongly disagree that this pattern is so important as to deserve the only remaining symbol. No way. https://stackoverflow.com/questions/2078978/functional-programming-vs-object-oriented-programming https://blog.learningtree.com/functional-programming-object-orientated-programming/ PLEASE DO NOT STEAL
|
Using the example provided above class Array {
#arrayData = [];
#example = 'test'
static isArray(obj) {
if (!obj) { return; }
try {
return !!obj.#arrayData;
} catch {
return false;
}
}
method() {
console.log(#arrayData)
// mutation
#arrayData.push(' :-( ')
#example = 'something else'
}
} Let's say there is some function From here we have the syntax sugar such that class Array {
private arrayData = [] // de-sugars to private(this, 'arrayData', [])
private example = 'test' // de-sugars to private(this, 'example', 'test')
static isArray(obj) {
if (!obj) { return; }
try {
return !! private(obj,'arrayData') // no sugar, however, for sugar `this` can be bound to something else
} catch {
return false;
}
}
method() {
console.log(private arrayData) // de-sugars to private( this,'arrayData' )
private arrayData.push(' :-D ') // de-sugars to private( this,'arrayData' ).push(' :-D ')
private example = 'something else' // de-sugars to private( this,'example', 'something else' )
}
} Or something along these lines... Wouldn't this address the aforementioned concerns? It is also explicit and readable using words rather than any strange syntax using In other words, |
Added benefits to using
// magic-library.js
export const external = function(privateFn){
privateFn(this, ... cool stuff.. )
... // more cool stuff
return whatever
}
// foo.js
import {external} from 'magic-library'
class Foo {
method() {
return external.call(this,private)
}
}
|
@babakness Your opinion against OO has nothing to do with that proposal. And FP is surely not the only way you go to develop a full computer software. But, like you, I'm on favor to use a keyword for the private scope instead of "#". |
@doodadjs You're misreading. I use OO as well. However, anyone who would benefit from a better use of I'm offering help in straightening this out, so please first review this and see if it covers the use case where this proposal wants to take away If so, see how this alternate syntax might be suited or offer insight in making it work. What do think? I feel that some of those who want |
It’s not “taking away” a token, it’s using one. “There might be a proposal in the future” is not a good reason to hamstring an existing proposal. Do you have a concrete suggestion for a better use of #? |
@babakness, @ljharb I really prefer the following structure : class Foo {
private user = 'doodadjs', // secret
private password= 'doodadjs2018', // secret
getList() {
return window.fetch(`https://api.mycomain.com/foo/list?user=${this.user}&password=${this.password}`);
}
}
const o = new Foo();
console.log(o.user); // access denied
console.log(o.password); // access denied But because "o" is just an object with "proto" set to Foo.prototype, I'm thinking that it should still be possible to do : o.user = 'mario';
o.password = 'mariobros'; To prevent this, may I suggest that the author of 'Foo' should do something like : class Foo {
@writable(false), @configurable(false)
private user = 'doodadjs', // secret
@writable(false), @configurable(false)
private password= 'doodadjs2018', // secret
getList() {
return window.fetch(`https://api.mycomain.com/foo/list?user=${this.user}&password=${this.password}`);
}
} |
@ljharb Do I have a concrete suggestion for # today, no. Does this proposal have a good use for it? No. Yes it is perfectly valid to not allow JS to be disqualified from the benefits of having a highly accessible I've made a suggestion above, you not address that. You're not being constructive. There is this fixation on using |
@babakness Private fields are private to the methods in their definition(1). I did an implementation of PRIVATE in doodad-js.
(Sorry if I use the wrong terms) EDIT: No... (1) should be : the class definition, sorry |
@babakness re: To be clear, we've discussed this and alternative syntaxes at extreme length over several years, and the more general discussion of private fields in JS goes back to at least 1999. We are aware that some members of the community find this syntax distasteful, but we can't constantly keep rehashing the same variations on the same several dozen alternative syntax proposals. |
@bakkot That's not just distasteful for me, but has a non-sense... Why scoping with special characters like '#' instead of a keyword like 'private'. That's weird, no ? |
@bakkot What did you reserved for "protected" and "friend" and others ? |
@doodadjs The language reserved a number of keywords a long time ago, well before we had an idea of how or whether we'd use them, because we thought we might later use them for features which had not yet been designed. |
class Foo {
thing = 4
constructor(){
console.log(this['thing'])
console.log(this['#'])
console.log(this['#foo'])
}
}
Foo.prototype['#'] = 'surprise'
const myCallback = (hastag) => console.log('outside',hastag)
Object.defineProperty(
Foo.prototype,
'#foo',
{ get: function() { myCallback(this[`#`]); return this['#']+'!' } }
)
const bar = new Foo() |
It's true that |
Wow. Super inconsistent. So why not a |
@bakkot forgot to tag you above. So why not a private function instead, introduced in the class like super. The first parameter is the instance, the second the requested field. Also, what about dynamically looking up private fields? Introduce yet another syntax? |
@babakness Because it would be sufficiently unergonomic as to make the whole feature not worth adding. I realize you don't agree with this, and at this point I doubt I will be able to convince you of it, but the committee has been convinced of it.
There is no current proposal to allow dynamically looking up private fields, and I am not currently convinced one is warranted. |
Ah, but the class {
private field = 'foobar'
} is sugar for some function private class {
private(...)
} Where you only reach for that function in the two lines of code where you want to check the field on other objects. If that is unergonomic, how does one will to declare or use any functions at all? It seems that these are the problems with the private fields aspect of this proposal:
@ljharb Giving up all that because its ergonomic is a good reason but if using A |
@babakness I am strongly opposed to any proposal which has |
@bakkot This is not correct, a |
We type orders of magnitude more functions out--the ergonomic debate is moot in comparison. |
The |
It's extremely unergonomic. This is like "why have operator overloading" in every language, because native syntax is better than boilerplate.
I'm not sure how this helps the ugliness argument at all. It's definitely worse when repeated. |
Perhaps I don't understand your proposal. What would this print? class A {
private field = 'foobar';
constructor(){
this.field = 'baz';
}
}
const a = new A;
console.log(a.field); If the answer is I am not particularly concerned with what is sugar for what. I'm concerned with the observable behavior of the code as written. |
@bakkot You'd use |
@babakness OK, that was my understanding. In which case my objection stands: I am strongly opposed to any proposal which has Your proposal has |
OK, it sounds like we won't be coming to agreement on a change in syntax here. Closing the issue for that reason. |
Forgot to update, I had a good conversation with someone on the TC39 committee. Looking at the Ruby syntax, it is true that it is clear that class / instance attributes are visibly distinguished anywhere they are used. Ruby's approach makes sense--to make something public you have to explicitly set getter / accessors. Sort of like exposing a variable in a closure through a getter. I feel that the current FAQ doesn't do a good job explaining the depth of the motivations well. That said, I admit that I'm leaning towards the syntax now given all parameters. |
PRs to the FAQ are welcome! |
@nevcos it makes perfect sense not to have all methods bind; builtins need to be representable with |
@nevcos There's plenty of people who like or at least are fine with the currently proposed sigil (I'm one myself and I've talked with a bunch of others). They just have little reason to be active in these issues. :) |
And it can continue to work. Assuming that everything is bound to the class instance, means that:
@jkrems: I'm sure that if you do a poll you'll have a good answer for that. |
@nevcos The following is another relevant alternative proposal-private-methods-and-fields. Not withstanding the unresolved design issues present with the current #sigil, additionally it might be of fruit to document and collect all the relevant push back/issues with/against the use of a sigil within the JavaScript community in addition to the poll you propose in order to collect the relevant data that could hopefully be presented at the next TC39 meeting and other relevant mediums. |
Just thinking again on this problem, we have already a way of having "real" private members by using
The only drawback that I see is that the members are not available on the subclasses. Thanks |
@nevcos Symbols are in no way private, and are in fact always fully public. Object.assign copies enumerable Symbols, and Object.getOwnPropertySymbols/Object.getOwnPropertyDescriptors/Reflect.ownKeys etc all expose symbol keys. |
@ljharb Ok, good to know 👍, thanks! |
Two reasons taste and utility.
So I confess that to me, this is just straight up ugly. Can there be something other than the '#' character? Why not just a keyword
Where "whatever" is a clean word that anyone reading would understand and is also not painful to the eyes.
The text was updated successfully, but these errors were encountered: