-
Notifications
You must be signed in to change notification settings - Fork 113
Can we take a step back? #144
Comments
You can accomplish this with fields and decorators: // class fields + decorators
function proto(memberDesc) {
if (memberDesc.kind !== "field" ||
memberDesc.placement !== "instance") throw new Error("Only valid on non-static fields");
memberDesc.placement = "prototype";
}
class C {
@proto propertyOnPrototype = 1;
} The biggest issue with declaring data properties other than methods on the prototype is related to shared instances of non-primitive values: class C {
state = { counter: 0 }; // on instance
incr() { console.log(this.state.counter++); }
}
class D {
@proto state = { counter: 0 }; // on prototype
incr() { console.log(this.state.counter++); }
}
const c1 = new C();
c1.incr(); // prints: 0
c1.incr(); // prints: 1
const c2 = new C();
c2.incr(); // prints: 0
c2.incr(); // prints: 1
const d1 = new D();
d1.incr(); // prints: 0
d1.incr(); // prints: 1
const d2 = new D();
d2.incr(); // prints: 2
d2.incr(); // prints: 3 In most OOP languages, state shared between instances is placed on the "static" side of the class. |
@rbuckton I think you may have missed the point of what I'm saying. Right now I'm saying that this: class Foo {
x = 1;
inc() { ++this.x; }
} being an equivalent for: const Foo = (function() {
var retval = function Foo() {};
retval.prototype = {
constructor: retval,
x: 1,
inc() { ++this.x; }
};
return retval;
})(); needs to be implemented before we go any further with any kind of private data proposal. Let's get public working first. |
Nit: They're configurable, writable, and non-enumerable.
This is an anti-pattern, and should be avoided at all costs. It's been the source of countless bugs in Backbone (which has a very similar class system). That class fields syntax does not allow you to do this natively is a great positive. If you want to shoot yourself in the foot later, mutate the prototype after the class' curly brace. |
I think having a way to declare instance properties is much more important than a dedicated syntax to declare prototype properties. The only way this could work without causing major usability issues would be for it to detect non-primitive (object) initializers and set those to This idea would indeed be taking a step back, but not for the better... The following comments might be a better fit for the other thread but...The problem with the “maximally minimal” approach is:
|
There's a much more straight forward solution than not using the prototype at all. In much the same way as class Foo {
x = {foo: 'bar'};
print() { console.log(JSON.stringify(this.x, null, '\t')); }
} into something like this: const Foo = (function() {
const isSet = Symbol()
var retval = function Foo() {};
retval.prototype = {
constructor: retval,
get x() {
if (!(this && (typeof(this.x) == "object") &&
this.x[isSet])) {
this.x = { foo: 'bar' };
}
return this.x.value;
},
set x(value) {
this.x = {
[isSet]: true,
value
};
},
print() { console.log(JSON.stringify(this.x, null, '\t')); }
};
return retval;
})(); The general idea here is to defer the setting of properties just like is being done for the current proposal. The only difference is that the property appropriately lives on the prototype, as is expected. |
As is expected by whom? Even before JS had classes I was already declaring data properties in the constructor and methods on the prototype. It would only be in special cases where I added data properties to the prototype. And while of course some people did things differently, this seemed to reflect most of the examples and common practice. And now many people have already been using public fields via Babel and having no problems with them, and the default placement of an instance property seems to work well for people. |
@mbrowne As you should be able to see from my previous post, there's a solution that gives you all the benefits of instance "fields" without going all the way to the "field" abstraction. I can't really see any reason why we need to go as far as something like "fields" to do what ES can already do with properties. |
@mbrowne There are going to be those who do things as you do, and those who do things as I do. The two groups will likely never be reconciled. However, the point is to take an approach that allows for both methods to be used. Embedding an approach that only considers one way or the other does a disservice to those using the opposite approach. What I've suggested above gives both. |
But current proposal just shoot yourself in another direction. Code sample which I already pasted several times and no one respond: class ExistingGoodClass {
get x() {...}
set x(v) {...}
}
class NowTrapAgain extends ExistingGoodClass {
x = 1
} Note, this footgun have different variant forms if you considered how code evolve. |
@hax @jridgewell The solution I'm suggesting avoids the gun altogether. That example would translate to class ExistingGoodClass {
get x() {...}
set x(v) {...}
}
var NowTrapAgain = (function() {
function getGetter(obj, field) {
var retval;
while ((typeof obj == "object") && !obj.hasOwnProperty(field)) {
obj = Object.getPrototypeOf(obj);
}
if (obj.hasOwnProperty(field)) {
retval = Object.getOwnPropertyDescriptor(obj, field).get;
}
return retval;
}
return class NowTrapAgain extends ExistingGoodClass {
get x() {
var data = 1; //This is the original assigned value
var getter = getGetter(this, "x");
if (this !== Test.prototype) {
if (!getter.data) {
getter.data = new WeakMap();
}
if (!getter.data.has(this)) {
getter.data.set(this, data);
}
}
return (this === Test.prototype) ? data : getter.data.get(this);
}
set x(val) {
/* It can be solved in the engine possibly, but from source, there's no solution that
* would allow perfect object re-instantiation given a data parameter, so changing
* the prototype value is out.
*/
if (this !== Test.prototype) {
var getter = getGetter(this, "x");
if (!getter.data) {
getter.data = new WeakMap();
}
getter.data.set(this, val);
}
}
}
})() The basic principle here is to split the property into a getter & setter, and allow them to manage the instantiation of the data. Done this way, neither of the foot guns you two mentioned exist. |
@hax To make sure we're on the same page, that snippet will not trigger the setter, it will define a new property with the value |
Technically speaking, I agree "proper encapsulation" is the highest priority in all scope of this proposal covered. And I sincerely hope it could happen.
As a man who already have writing JavaScript for 20 years, and plan to keep writing for another 20 years, I don't care waiting another 2 years, because if you landed a broken solution, I would suffer 20 years. To be honest, the semantics of the private part of this proposal is ok to me. I even considered the But as I already said, after I gave a speech, and got the feedback, I just realized, we should never underrate the risk of the community break. And even I see some other controversial issues in other proposals, the risk of no one can compare to this. Why? Because there are already too many solution for private in the wild! If you just land a unwelcome proposal like this, you are just add another mess even you are literally "standard". This is especially true in TS land, because TS already have an official, solid, programmers familiar private solution. You can see my analysis in other thread.
No. They are not. Public is not essential as private. Actually public field has been proved as a bad idea in other OO languages like Java, C#. Though the reason why they discouraged it not necessarily applied to JS, if you checked deeply, you just found more issues in JS than in Java/C#. Note, the historical property usages in JavaScript is not equal to "public field declaration" you introduced in this proposal. There is a subtle but significant difference, that you provide a footgun that subclass could easily (and accidently in almost all the cases) redefine a property which is already defined by base class. This footgun is never available in the past unless someone explicitly write |
Yes you know it define a prop, but average programmers just think he do the same as |
The privacy in TS does close to nothing, because TS users can just opt-out of types to use private APIs, and it doesn't affect anyone else. Moreover, TS was never meant as a competing standard, so the image is not relevant, and it's getting a bit too spammy. |
There are some options to solve this:
I think you don't want to any of them. So just let the footgun shoot ourselves... 😭 |
Ok, you can keep your judgment which have no any proof. Or you should ask TS guys how they think about it. |
Sorry, proof about what? Here's TS playground demonstrating privacy being stripped away in JS and being optional in TS. Here's TS design goals, which include tracking JS. |
@rdking: Field initializers cannot be lazy by default as you propose in #144 (comment) as it would cause strange side effects and unexpected behavior: let idCounter = 0;
class C {
id = idCounter++;
}
const a = new C();
const b = new C();
b.id; // 0, but expected 1
a.id; // 1, but expected 0 The semantics of field initializers mimic the behavior of field initializers in Java/C#, in that initializers are processed in the constructor after superclass constructors have been evaluated. Privacy in TS has been well known to be a "soft" private, relying on design time behavior to inform the user that the API is not safe to depend upon. It is not and was not designed as a security feature, but more of a way to indicate to users parts of an application that you should not take a dependency on. It is akin to |
Note that you could, however, create a decorator that performs lazy initialization. However, unlike the current TS behavior (which uses Set and would trigger setters on the prototype), the class fields proposal uses CreateDataPropertyOrThrow (which won't trigger setters on the prototype). |
Ok if you are talking about hard private, I never against it. I just want to say, the difference between hard private and TS compile-time private doesn't very important in practice, at least in most cases. And, I think TS eventually should move to one private, it should be JS native private, as you say, the design goal of TS is to follow JS semantic. Because of that, you should realize the good proposal should not only consider JS, but also consider TS. If a proposal can not satisfy TS programmers or bring them pain, they will keep using TS compile-time private. Could you get my point? |
I'm afraid C# has different order with Java. I prefer C# but it seems current proposal choose Java? |
@hax: How do they differ? While I admit I've spent considerably more time in C# than Java, the initialization semantics in https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5 seem fairly similar to C#'s semantics. |
class ExistingGoodClass {
get x() {...}
set x(v) {...}
}
class NowTrapAgain extends ExistingGoodClass {
x = 1 IMO it would be better if redeclaring the same property on a subclass in this way were illegal. It’s a recipe for confusion and problems. Overriding the getter and setter would probably be OK but overriding it with a public field declaration is an issue. I’m also against this: class Superclass {
x = 1
}
class Subclass extends Superclass {
x
} |
@rbuckton Given your example, I'd expect let idCounter = 0;
let C = function() { //There's no constructor in C! }
C.prototype = {
constructor: C,
id: idCounter++;
};
const a = new C();
const b = new C();
b.id; // 0
a.id; // 0 Justification? Currently everything inside the |
@rdking: That is not how class fields behave in any other OOP language. If those initializers defined properties on the prototype, the feature would be a major foot gun and pretty much unusable. Prototypes should define instance shared behavior. Even with classic ES5/3 "classes", defining non method or accesor members of a shared prototype was a foot gun and generally to be avoided. |
@mbrowne From a different point of view, I agree that
But the problem is that the re-declaration is a "field" and not a property. If the assignment remained in the constructor, there would be no problem. However, assigning a value in a definition sets up the expectation that the value will be present before the first line of constructor code is run (that includes Put simply. If you want an instance field, then make it after the instance is created (i.e. in the constructor). However, if you want a |
@rbuckton You might want to double-check your facts. Both C++ and Java work that way. Values assigned to properties of a class are done so statically so that an internal prototype of the exact shape of a class instance can be created. This is why |
import java.util.*;
import java.lang.*;
import java.io.*;
class Main {
static int idCounter = 0;
static class C {
public int id = idCounter++;
}
public static void main (String[] args) {
C a = new C();
C b = new C();
System.out.println(b.id);
System.out.println(a.id);
}
} output:
Sidebar: I'd really appreciate it if you would create and run (and ideally provide) code samples before making claims about the behavior of existing languages. |
This is a big difference between a typed language like C# and an untyped language like JavaScript. The difference between ECMAScript and C# constructors, is that C# does not allow you to evaluate Statements prior to calling the base class constructor: // c#
class C {
public int x;
public int y = 1;
public C(int x) {
this.x = x;
}
}
class D : C {
public D(int x): base(x) {
// cannot execute statements before `base(x)` is evaluated.
Console.WriteLine(this.x);
}
} vs // js
class C {
x;
y = 1;
constructor(x) {
this.x = x;
}
}
class D extends C {
constructor(x) {
// can execute statements here, but `this` is not initialized yet.
super(x);
console.log(this.x);
}
} |
Sorry, the question wasn't there when I wrote my reply. Anyway, you'd have to ask whoever made that change; I don't believe such a change was discussed in committee. (And at a glance it looks to me like decorators still imply [[Define]] semantics, so I'm not sure what you're referring to.) All I was claiming was that the question of which semantics to use for class fields was indeed discussed, at length, before stage 3, and TC39 came to consensus on [[Define]]. Otherwise it would not be at stage 3 with those semantics. |
Oh, sorry, I misread - yes, it's proposed that decorators would allow [[Set]] semantics as an opt-in. This seems sensible to me. The default, and the semantics for undecorated fields, remains [[Define]] I guess I don't understand what you're asking. |
@ljharb but what if some issues appeared only AFTER consensus and those issues may lead to objections from some committee members if they knew them BEFORE? |
To respond to the original post: I think private fields (and methods) are well-motivated to provide encapsulation. I understand if you want them to be delayed, but I think we've thought this through well and have a good design. Advancing the proposal to Stage 3 signified that TC39 agreed, and I've heard a lot of positive feedback from JavaScript developers who I've met, even if the discourse in this repository is more negative. On the Set vs Define question, which this thread seems to have veered into: please see a description of the resolution of this issue in the explainer. |
No one has doubted the "motivation". We are all here discussing the issue because the motivation is undeniably good! We simply believe that the chosen implementation leaves far too much on the cutting room floor in trade for the scarce few benefits we stand to receive. The simple existence of other possible implementations that leave far less damage in their wake should be more than enough to make TC39 want to reconsider their options. For example, proposal-private-symbols only has 2 issues:
At the same time, it has a far smaller feature set than class-fields. However, there's proposal-class-members, which has a superset of the features of class-fields with none of the issues. The only potential issue it can be said to have is that its syntax is slightly more verbose. That was necessary to solve some of the ASI issues caused by class-fields. As an added benefit, the mental model is just the combination of closures and class instances (which I understood to be the conceptual goal of trying to add private data syntax).
Has TC39 thought through class-fields well? There's no doubt the answer is yes. Did you come up with a good design? As viewed within the limits of the paradigm chosen for class-fields, again yes. However, as viewed without such blinders, NO. As I've said many times before, while each individual issue is not enough to warrant even thinking about slowing the progress of class-fields, the combination of those issues greatly exceeds the value we stand to gain from the proposal.
This is a process flaw, and an incorrect assessment at the same time. Advancing the proposal to stage 3 merely signified that no one was willing to risk losing political capital over hurt feelings caused by openly vetoing the current proposal. Such a loss risks causing proposals by the vetoing board member to fail gaining traction due to emotionally based counters by the offended parties. Let's call a spade a spade here.
Of course you'd hear a lot of positive feedback from them. You've given them something they want. Wouldn't you be happy too if someone important in your community handed you a platter of you favorite snack? But how would you feel if 2 hours after eating them, you felt sick on the stomach and had to spend the next 24 hours on the toilet? That's what this proposal is like. It looks good up front, but you won't experience the negative consequences of it until you start trying to use it for something non-trivial. Who's going to use this proposal for something non-trivial while it's still in development?
Doesn't the discourse in this repository represent the view of those who feel they have a stake in the outcome? Let me go back to one of your other analogies. If every house on the street has a dog that sleeps outside, but your dog (and only your dog) is barking tonight even though you didn't find anything wrong when you looked, what do you do? You've only got 2 choices:
Right now, TC39 is doing the 2nd. Unfortunately, if you push class-fields to stage 4, even though you're currently on good terms with the neighbors (positive feedback), eventually their dogs will start barking too. Unfortunately for all of us, it'll be too late to fix the problem then. |
Opinions based in emotion are utterly valid, for the record. Emotions matter, because we're human beings. |
That's nice and all, but you're basically telling us all that in order to fix the inheritance problem TC39 is pushing on us, we're going to have to wait several more years for a proposal (that's already been stuck for several years) to reach stage 4. Until then, the feature is useless to those of us who need to reliably use inheritance. That's not a resolution. That's a brush-off.
Please don't take what I say out of context to make your points. I was referring to the type of negative argument that gets raised by a person due mostly to a personal dislike for the opponent. These types of arguments usually never come about except due to an emotional context that has nothing to do with the topic. If there's emotion due to the topic, of course it's valid. However, much like the varying degrees of dislike and disgust shown for the unfortunate syntax of this proposal, rationality matters more than emotion. I can't argue against the syntax of this proposal no matter how ugly it is. Why? It is the way it is due to limitations of the language and paradigm used by this proposal. What all of us dissenters have been trying to get through to you of TC39 is that your paradigm is flawed and leads to problems that are going to affect us all negatively. We (including some members of TC39 itself) have been trying to present new paradigms that can accomplish exactly the same goals as this proposal. However, as a whole, the board has not shown much(if any) interest in listening. It has only been a handful of you that seem to think it worthwhile to even entertain these alternate possibilities, and for that I think we are all thankful. However, the fact that those of you who are indeed listening are either the champions of the current proposal, for which you have an obvious vested interest, or dissenters who are not willing to risk their political capital on the board to veto this not-good proposal, it doesn't do much good for either us as individuals voicing our concerns, or the language and community as a whole who are about to suffer an irreversible slight. You asked me once before if I would be satisfied if TC39 were to really give all candidate proposals more consideration. I'll answer you here once again. I would not be satisfied if it did not result in the unseating of the current proposal. But I would be at least more content in the knowledge that due consideration was given to the alternatives. If TC39 were to come up with clear, insurmountable reasons why the alternatives are insufficient, then I would no longer have the ability to argue, despite my dissatisfaction. That alone would represent a massive improvement. |
I am not convinced that the private symbol based approach will be viable either, but I have left those issues open since there is a lot of new discussion on it, it will likely be coming to TC39 soon, and I haven't yet documented my concerns thoroughly. I am not sure if I agree with your analysis of the process flaws, but this is a really complicated issue. I don't think it's quite too many or too few vetoes, but that the use of vetoes (or veto threats) is unequally distributed within the committee, and this influences how tradeoffs are weighed. |
@littledan Admittedly, the private symbol-based approach as defined by @zenparsing, is not as feature-rich as class-fields. At the same time, it is far more self-contained and only suffers from 1 real issue (the fact that it can leak private keys). Even this issue isn't insurmountable and can be handled without additional language modification. Class fields has numerous issues. Even if each issue individually has a severity of 1/8 the value of this proposal, there are more than 8 issues. The value of this proposal is more than consumed by the issues it contains. Sure, few have run into them with any real severity yet. However, few have actually tried to write any production-level code using class-fields yet. This is one of those situations where your current level of presumption is likely going to come back to haunt you. I hope that's not true, but looking at how many of my current design patterns this proposal is about to break, I don't have that much hope.
Meanwhile, we developers get stuck with something that poses problems we can't even work around, just because of some political bologna slices. What happened to doing things that are in the best interest of the community? Let me set all of that aside and say this: I'm glad some of you are giving private-symbols a good close look. I don't have any confidence it will be able to supplant class-fields either. But it's still a better overall proposal. Have any of you given any thought to class-members at all? Sure, it began as a continuation of classes-1.1, but has since evolved long past that point. The current incarnation supports every capability provided for in class-fields but without any of the trade-offs that we've apparently been re-hashing to death. Even if it cannot be considered as a replacement for class-fields, TC39 should study its principle design. Maybe if you do, you'll figure out how to solve the problems in class-fields without leaning on a proposal that isn't even close to guaranteed to be released. |
I've looked at it some. There didn't seem to much anything in it that hadn't already been discussed at great length. For example, I think overloading |
If you don't like I never understand why you always use a bikeshed issue to dismiss a proposal but never apply same standard to current broken proposal. |
Funny. That's not how it works at all. Each instance carries it's own closure in the same way as an object with functions returned from a factory function carries around the closure of the factory function. The analogy is exact. Anyone who understands object factories would also understand this concept. Given that it's a much older concept in ES than
And that wouldn't change, so I'm not really sure what you're talking about here.
"Closure access operator", "private member access operator", I don't care what its called. I only named it that because I felt it fit the scenario. If enough people think it's a confusing idea, it can be renamed. The purpose doesn't change though. Again. I'm interested in hearing more of your thoughts.
Probably as much as I am opposed to introducing syntax for adding data properties to something that's not a product of the
More or less, and we don't disagree as much as you would want to say. Here's a question for you. Given any particular issue (doesn't matter what it is) which is more important, an emotional argument about how just 1 person feels about the issue, or a rational argument about how the technical details of the issue will affect the community? Like I keep saying, I'm always willing to discuss issues with those who keep an open mind and can speak rationally and logically. Emotion only has a place as an argument when there is no rational or logical argument to be made. Don't "like" the keywords? Change them, but not to symbols because that smacks of operators when we're trying to define declarators. |
@rdking These two statements cannot both be true:
because the variables a single function sees do not depend on how the function is invoked, which means that if there is only one Re:
I would have to have a much longer philosophical conversation which I am not really up for having before I could explain why I don't consider this statement to be well-founded. Please let's just drop the topic of emotion vs logic. |
@rdking After this comment, I'll avoid interjecting too much in the non-technical parts of this discussion (to avoid adding unnecessary noise), but I just want to point out that your characterization of the quantity and severity of issues in this proposal as it compares to alternatives is highly opinionated. If the committee agreed that alternative proposals had far fewer issues and that those issues were far less significant than those of class fields, then I hope it's obvious that they would not continue to back the current proposal. I sympathize with your frustration at trying to decipher some of the committee members' more terse responses to certain questions, and it's always ok to ask for more clarification, but everyone here has valid reasons for their position even when they don't explain every point at great length. And where they disagree it's because they have a very different perspective from you, not because they're being intellectually lazy or the other things you're implying. It's unfair to conclude that the only possible explanation for their decision is that they're irrationally neglecting to take all the issues fully into account. I'm not sure whether it's your honest opinion or more of a debate tactic to challenge them to explain their reasoning in more detail, but in any case I think it's unfair and really not helpful. If I were you I would be frustrated too, especially given that there hasn't been more of a direct critique of your class-members proposal (or even a detailed public critique of classes 1.1 that I'm aware of, although I'm sure there was more discussion internally in the committee, and also many of the discussions here have touched on it in various ways). But obviously the committee has spent a great deal of time engaging with you and other members of the community, and I don't know why it's so hard to conceive that they simply disagree; you obviously prioritize things very differently and come from a different perspective for valid reasons just as they have valid reasons. It may seem like I'm saying "everyone has valid reasons" just for the sake of reducing tensions, but that's not it. I genuinely think you're being unfair and uncharitable to paint the committee in the way you have here. |
The criticism of using closure-like syntax for instance-specific state is not "bikeshedding". For me, it's one of the most important reasons that I prefer this proposal to classes 1.1 and class members. I believe that |
@mbrowne I get what you're saying, but... As my wife has explained it to me, I have a particular pet peeve for unnecessarily irrational arguments. So you'll have to excuse me if I present a rational or logical argument, get countered with an emotional argument, and find myself frustrated. I am probably incapable of comprehending why an emotional argument would ever be deemed more important than a logical or technical argument when it comes to a communications form like a programming language. A computer cannot process our emotions. It can barely process our intentions. Even then, it can only process our intentions if they are specified in a clear and rational way. To this end, our emotions should not matter nearly as much as the technical and use case details. First make it work. Then make it look good. You can always make something ugly look good later. That's why this horrible syntax is actually an acceptable issue for me. However, a machine that is broken by design is extremely difficult to fix after its been built. Do I think TC39 has been intellectually lazy? NO! Not even close! Believe me when I say I understand how much work it takes to make something even reasonably close to workable out of a bad design. I have had to do that at work more often than I care to admit. What I would like are answers as to why there are so many cases where the emotional argument has outweighed the logical or rational arguments that dissented. I already know the opinion of TC39 members. I want to understand those opinions. If I can be helped towards an understanding that TC39 has not made a form-over-function choice (which is what this proposal looks like given what's been revealed so far), then I think you'll find my responses more palletable. Until such time, understand that despite my penchant for logic and rationality, I too am an emotional being and will show that emotion as I am so moved. In all honesty, I've done well to hold back as much as I have given. I've fought hard against myself to keep the exposure of my frustration to a minimum. I do not believe I'm asking for too much. I'm also not the only one seeking these answers. So I don't believe my frustration to be unwarranted when after so many long years of asking questions, the answers are still not forthcoming. |
@rdking, I've been doing my best to answer your questions and the questions of other people for several years now. I'm sorry I haven't been able to articulate answers such in a way that we are able to mutually understand each other. I'm not sure what else to do. |
I don't want to get into a fruitless debate about this, but I have seen very few comments from committee members saying things like, "I don't like it" without providing or citing a logical explanation (however ambiguous or lacking in details). I do get your frustration, but this is more of a communication problem than actual non-existence of the logical reasoning (including the bigger-picture "why" reasoning) you are looking for. |
@bakkot For the first time, I think we feel the same way. 😆 I'd still like to know what type of thing that @mbrowne If it's a communications problem, color me stumped as to what got missed. I've worked very hard to analyze every argument given to me. Every bit of new information gets processed. I'm sure the same has been true for the TC39 members I've argued with. If the information I needed were present, I either accepted it or refuted it with more logic. That's just the way I argue. Either way. I guess it doesn't really matter any more. |
This is the thing: if you ask a question, get an answer, and then explain why you disagree with it, this is different from not having the "why" presented. |
@littledan I recognize that. Like I said before. The "why" that I'm looking for is the why X is more important than Y". Notice how for most of those questions, I'm not among the ones repeating those wheels? That's because I've accepted the fact the difference in our opinions cannot be surmounted with logic and reason. I'm not saying that TC39 has been unreasonable, but rather that I can't fathom your sense of reason on some of these issues, hence the questions. When that reason comes down to an unverified, unwarranted impression that's refutable with logic, its outside of my ability to comprehend. Even if I cannot accept your reasoning, as long as I can comprehend it, that's good enough. For the cases where its just a matter of opinion or perspective, I fail to understand why a side is taken when it would be more prudent to either absorb all cases or neither. For instance, the [[Set]] vs [[Define]] debate. Why not provide syntax for both? Currently that's the reality. Anyone can either set or define a new property at any time. Why promote one over the other knowing that it will lead to misunderstandings and bugs? I get it. You don't think that is likely to happen. Yet there is plenty of anecdotal evidence that it will. Compound this by a quote from @ljharb: "If it can be done, someone will do it." Follow that with another quote of his: "A non-zero risk is unacceptable". While I admit to taking that last one out of context, the inconsistent way that values are being applied are yet another point of confusion, and the main reason there are so many questions in the first place. I don't expect you to try and address any of that here. You're working on a document that should have this information, right? My comments here are only meant to help you understand the type of information that can help those who don't find this proposal acceptable. What is needed is the information on why X was preferred over Y, with an understanding of the undesirable consequences of both X and Y. Its a lot to ask, especially since this information wasn't gathered at the time it was being discussed. Process-wise, if this kind of info were collected for future proposals, it would help not only the community to understand the thoughts going in, but possibly even give TC39 a better perspective on the concerns of the community. It's just a thought. |
TC39 does a lot of reasoning from first principles, but there's no sound first-principles answer to the question of how different goals are weighed. The README and FAQ do their best to explain why the goals are taken as very important, but there's no secret document that I am working on that will convince you that these goals are more important than other goals. Ultimately, we have to make a subjective call, and the way we make the call is based around TC39's committee consensus process that we've been discussing. What I'd like to do going forward is improve outreach to collect data that can be persuasive to the committee and influence their weighing of goals. I'd like to focus on earlier stage proposals, where the committee members' positions will be easier to influence. This will be a gradual process, and it will only be possible if we can work collaboratively and constructively. |
@littledan Just a suggestion for the future: A common way of making subjective calls in a more deterministic fashion is to create a scoring and classification system. This gives a reasonably well-defined weighing system for goals. It becomes less of a surprise to the community if the weighing system is understood. Even if we cannot agree on purely subjective matters, a scoring system leads to results that are difficult to question. If you'd like to see such a system in action, we can review this proposals benefits and issues using the scoring system I have for proposals. I even used this system when developing class members. |
Not sure what you mean by scoring and classification. Do you mean listing out the pros and cons, and documenting these well? Or going further and giving numerical weights to those, to make the call? The former seems like a good idea, but I haven't heard about any experience using the latter. |
@littledan I mean numerical weights. For instance, here's why I'm not particularly a fan of this proposal: The System:3pts for a logical or technical argument Scoring is positive for features and negative for issues and trade-offs The Evaluation for Class Fields:Features+3: Provides encapsulation Issues-1: Unpalatable, non-ES-like Syntax Overall Score: -7 (Bad Proposal)There's no way I wouldn't veto this proposal if I was a board member. As you can see, the syntax issue by itself wouldn't have been enough to even bat an eye at. If the list of issues were half as long, this proposal would still have my support. No 1 particular issue is anywhere near enough by itself to warrant stopping. But the collection of issues is just ridiculously damaging from my perspective. I know you can probably add a few more things to the "features list", but I have other things that I can add to the issue list as well. The point is that I believe if TC39 also used such a system to weigh these proposals, there would be a somewhat surprising change in the "consensus". |
Numerical weights sound game-able and inviting lots of lawyering. Do you have any example of successful use of such a system? |
@littledan Is it game-able? Yes, especially if the conditions for scoring are not deterministic enough or subject to interpretation. For instance, I scored "Interferes with upstream inheritance" as a technical problem but "Interferes with downstream inheritance" as a use-case problem. I very easily could have listed both as either technical or use-case, but no one would expect an upstream inheritance issue, hence technical, while downstream could potentially be explained away with the override concept. That's why this is where your(TC39's) use of "consensus" would be handy. The consensus would be need to settle on how to score each feature/issue. Once that's out of the way, everything else becomes significantly more deterministic. As for examples, look into AGILE development. What I've described for you is the process for how tasks are weighed. Everyone first agrees that some simple task is given a particularly low numeric weight. All scoring is then done relative to each developers understanding of the difficulty compared to that baseline. A short discussion leads to a consensus on the weight for that task. |
When there is so much feedback on this proposal from those who are aware and choose to speak at all and from members of the TC39 board itself that there are several things wrong with this proposal that make it a less than adequate solution to the problem of adding data privacy to ES, isn't that a good enough indication that it may be time to step back and re-think?
How about, instead of trying to push through with class-fields, we come up with "class properties" instead? The general idea is simple. Objects have properties. Functions are objects. Classes are functions with a prototype object. Therefore classes should support declaring properties for both its function and its prototype.
Can we solve and add this to the language first, separately from all other concerns? No private, no protected or other intermediate. Just ordinary public and static properties. I'm beginning to come to the impression that until this much is solved, the path forward for a reasonably acceptable, non-future path blocking proposal won't easily reveal itself. Maybe we're trying to take too big of a jump all at once.
The text was updated successfully, but these errors were encountered: