-
Notifications
You must be signed in to change notification settings - Fork 0
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
Reflection #11
Comments
If you want to allow your private data to be reflectable, you need to explicitly expose that capability. Just like you can't reflect on a function's closed-over variables, there shouldn't be any way to reflect on a class's private data. |
@ljharb I agree that it would be best for reflection to require explicit opt-in. But it would be good to have a concise way to enable reflection on all fields in a class in one fell swoop. That would be an optional feature as well--the ability to expose some fields for reflection but not others is a great feature for certain use cases. |
so, like, an |
Would it be trivial to write such a decorator that adds the fields to the "ownKeys" list without making the property data itself publicly accessible? If there were going to be a way to reflect private fields, it shouldn't compromise the privacy of direct access through the object. Only the method used to reflect them should be able to gain access. At least, that's how I see it. |
It would be trivial to keep them private but expose them to some different reflection method, sure. There’s lots of ways to build that API. |
@ljharb A decorator could be a great solution, but my understanding is that recent changes to the decorator spec make it impossible to implement a class-level decorator that would expose all the fields (including private fields) in the class to a reflection API. I'm guessing those considerations (security and encapsulation concerns about class-level decorators accessing private fields) would probably be similar for this class-members proposal. Of course, it would be possible for a transpiler to conveniently solve this use case by creating all the field-level decorators for you behind the scenes, but I hope that we won't always have to depend on transpilers for this. As long as the author of the class has a way to clearly indicate that they are deliberately enabling reflection for the class and all its members, it seems to me that it should be entirely possible for this to be a native feature. |
That’s true; a follow-on addition to decorators would be required. |
@mbrowne For the original formulation of this proposal, it would be flatly impossible for decorators to act at all on any private member. For the new suggestion in #10, there's no reason why decorators couldn't work on private members. The reason class decorators have been restricted from accessing existing private members is that decorators are subject to man-in-the-middle attacks. If a decorator on a class containing critical data or functionality in private members were substituted, well, I'm sure you can understand where I'm going. For that reason, even under the new suggestion, this proposal would maintain that restriction. |
All other mainstream languages allow class private be reflectable by default. So I stole your sentence: If you want to disallow your private data to be reflectable, you need to explicitly forbidden that capability (by some security mechanism, like in other languages, or in JS current mechanisms, we definitely can leave it to decorators.)
Many languages also have closure/lambda, but their classes private still can be reflectable by default. |
This is very problematical. Decorators proposal have a big risk that it's too complex and too powerful. For example, decorators can change a member from non-static to static easily. Such power have no use case and harmful to the code review (how a reviewer could expect a decorator can do such thing which totally change the very essential semantic?) and normal tools (like linter) can not forbidden it. I think a large reason why it need such complexity and unnecessary power is class fields proposal leave too many things to decorators. It will have a big cost in both author and user side of decorators. |
@hax i consider it a mistake that all other languages allow such reflection; I'm glad JS won't be making that mistake. |
@ljharb This is a too arrogant assertion. I have no word to say. |
@hax it's an opinion, not an assertion. |
Ok, you can make any opinion to disvalue a design of all other languages. I can make my opinion that your such opinion is useless. So how we can have any meaningful communication? |
Fair. I'll clarify: I've seen countless production bugs caused by misuse of allegedly "private" data. npm broke node itself once by deleting an underscored property, thinking that nobody would be relying on it. The design of JS is that you can't reflect on closed-over variables. Private fields are intended to grant the same privacy afforded by closures, to instances. Reflection on them would be weakening their encapsulation, and breaking the intended similarity to the encapsulation provided by closures. Can you help me understand what your use cases are for reflection, and why it requires reflectability to be a default? |
First, you need to respect the designers and the committees of all other programming languages, and also respect the practice, experiences from the communities of these languages. Actually many JS programmers also use other languages and be a part of the communities of different programming languages. I never say we should adopt a design just because all other languages adopt one. But I ask all guys in TC39 for the basic respect to other guys. They may made mistakes. Every languages have their own troubles. Just like TC39 may made mistakes and have our issues. It's ok that one or two or even many languages can made bad design decision, but it's too arrogant to believe all made the same mistake and no one realize it. If you have such basic respect to others, you should ask yourself why all other programming languages and their community do not have "countless production bugs caused by misuse of allegedly private data". Of course I have the answer, but I think it's very easy to discover by yourself if you can throw your arrogance. Actually I already answered such question in previous comments.
I also already answered this question before. But I'll simply repeat it again: this is the balance between the library authors and users. Actually, JS (up to now) and many dynamic languages like Python/Ruby etc. give more power (everything can be reflected and controlled) to the library users and give less power of "restrict their users" to library authors. You can find many articles in the internet to advocate such philosophy (let the programmers do anything they want). To be honest, I am not the fans of such philosophy, that's why I am very happy that we can have private in class. But it's very weird that a language like JS suddenly go from one end to another extreme, even severer that all of other languages. Too dramatic. You force the JS programmers (both library authors and users) do the difficult choices:
This is not a good thing to the ecosystem. |
Yes closure have hard private (just like all languages have closure/lambda but they all not do the same thing). The interesting question is if we already have it, why add it to class? Ok, closure may not cowork good with classes, but we also have weakmap which very ok for class usage. Anyone who want hard private and don't hesitate to break proxy, prototype-inheritence, reflection... could just use it. I'm fine with it. I think all programmers are fine with it. But make classes by default hard private, branding, and force all programmers who only want private (do not care about hard, do not care about branding...) imperceptibly break many other language facilities like proxy etc. is just another story. And, if we really want to introduce closure-like private, we should use classes 1.1 like syntax which have very good mapping to old closure syntax. But now we are using |
If you want to be able to use reflection, what advantages would private fields have over existing symbols? |
@nicolo-ribaudo Symbol-based semantic can solve 50% issues (keyword-based syntax can solve another 50%), all current language facilities and patterns still work, and simplify other proposals (like, decorators). And we still have the flexibility of opt-in hard private, branding if we want. |
It's very logically flawed to believe that just because a lot of other educated and thoughtful people did things one way, that it's the right way to do it. I think we fall into the trap of enshrining experts too often, and accept flaws and miss opportunities to correct them when designing and developing software, including language design. Of course, when a lot of other languages do things a particular way, it should definitely give us pause if we think we know better; it should remind us to do our research very carefully and wonder if we are missing something. But if we research and consider all the factors we can and still find flaws in how another language does something, we shouldn't jump off the same bridge. Even if it's somewhat subjective (i.e. there are objectively both significant upsides and downsides), each language has the right to decide for itself. Also, let's remember that even the inventors and earliest users of OOP have strong disagreements with how modern languages have implemented it. In some cases their opinions evolved over time and they no longer agree even with some large aspects of Smalltalk that seemed like a good idea at the beginning. Ironically, some of the biggest defenders of so-called OOP are much more fixed in their opinions, believing they are protecting a legacy. Trygve Reenskaug (one of the earliest users of Smalltalk and the inventor of the MVC pattern) no longer believes it is a good idea for an object's behavior to be permanently and only determined by its compile-time class, as is the case in Smalltalk and Simula (and according to James Coplien this was done just as an"expedient of implementation" [1]). (For more details on Reenskaug's current vision, see http://fulloo.info/Documents/CommSenseCurrentDraft.pdf.) Consider also this quote from Alan Kay:
(from the OOPSLA 1997 presentation “The Computer Revolution Hasn’t Happen Yet”, as quoted at https://ivanovivan.wordpress.com/2010/09/13/alan-kay-quotes/) Another one from Kay:
(from https://www.computerworld.com.au/article/352182/z_programming_languages_smalltalk-80/) I also question your assertion that just because other languages enable reflection of object properties by default, that their designers and current maintainers universally agree that it's a good policy (in fact I remember someone recently citing evidence in one of the class-fields discussions that at least some big names in other languages wish that privacy weren't so "soft"...I don't recall if the people saying that were language spec writers, but this assumption should be verified). @ljharb already clarified that his view on reflection is an opinion. I haven't firmly made my own conclusion yet, but I said that reflection "can and perhaps should require explicit permission from the class author" based on the merits of that argument, in my opinion. Implicitly enabling a feature that weakens encapsulation (which, BTW, is of utmost importance to Kay, Reenskaug and others—and in the spirit of not just relying on experts is a very well-grounded principle that I think we all agree on) seems like maybe not such a great idea. Yes I know that access modifiers were never intended as a security feature in other languages and I don't think they need to be a security feature in JS either, but still, reflection does provide a loophole. I'm well aware that the designers of languages like C++, Java and C# have much deeper expertise than I do in probably every relevant area, but I still don't think it's necessarily arrogant for me to consider disabling reflection by default, especially when apparently most members of the TC39 committee (who are much more knowledgeable about many aspects of language design than I am) agree with this. [1] https://groups.google.com/d/msg/object-composition/Zc-aVmFpIgA/AK0uu1ItBtwJ |
@nicolo-ribaudo The semantics of symbols are a great fit for soft private. The only problem is that they are quite verbose and inconvenient to use for this purpose. So in practice I think it will be better to use decorators to expose private state...also for reasons of consistency (i.e. all object properties/variables/fields are declared in a consistent way). |
Doesn't this already work? const foo = Symbol();
class C {
[foo] = 2;
test() {
return this[foo];
}
} If you instead want private symbols, what is the difference between "private symbols with reflection" and "public symbols"? I know that also the |
@nicolo-ribaudo Second, the syntax of |
@mbrowne Thank you for your comments.
Do you really believe current proposal has considered all factors ? I hope you can be fair enough to see all the issues current proposal have, just two examples:
I beg TC39 first solved such unique flaws in field proposal before you can have confidence to say there is a flaw in all other languages...
JavaScript is not a niche language, so it will have a big cost to the whole community and ecosystem if you decide something very different to all other mainstream languages without solid, objective reasons. I think subjective is unacceptable. |
You should read the first paragraph you write:
If you choose to believe some members of TC39, you should also notice there are members of TC39 never agree current class fields, so why you not believe them, because they do not have more knowledgeable about many aspects of language design? 😜 After all, I'm not strictly against hard private by default (if we do not break proxy by default and provide a way to opt-out hard private and branding), I just want the guys in TC39 can respect the feedbacks and rethink of current broken proposal. We should at least allow some other alternatives to forward stage 0, give a chance to both community and the committee. |
@nicolo-ribaudo Yes, public symbols work for private state that should have reflection enabled. Maybe it's an overstatement to call them "quite verbose" but I think we can do better than effectively having to declare every private member twice for reflectable classes. Also, for a class with both public and private members, I think the inconsistency of [] for (soft) private and dot notation for public isn't ideal. After we have property/field/variable declarations (most likely class fields) I think using symbols for this will be regarded as a workaround rather than a proper solution, especially considering that there will still be classes for which the author doesn't want to enable reflection and especially if reflection is disabled by default. Because then you end up with two different kinds of syntax for a very similar concept, instead of the same syntax with some way to turn reflection on or off. I also don't think continuing to use underscore-prefixed properties for soft-private will be regarded as a proper solution for the same reason (of course, underscore-prefixed properties are actually fully public, which is probably the stronger argument against them). |
I agree. The objective tradeoffs should be fully considered. But deciding which pros and cons are most important often involves a mix of objective and unavoidably subjective aspects.
I never said I don't value the opinion of those on the committee who disagree with the class fields proposal. In fact I think their voice is critical and I am very glad that not everyone in the committee thinks the same way and always shares the same opinion. In particular, @zenparsing has made huge contributions to the considerations and discussions about class fields and alternatives. I very much agree with some of his points. My opinions are not just based on the majority opinion of the committee and it's odd that you would think that given some of the objections I've expressed to the class fields proposal. |
Zero members of TC39 disagreed enough with the current class fields proposal to object to it reaching stage 3 - some have reservations, sure, but there is consensus - because a single person is enough to block anything, that means that everybody on the committee agreed with advancement. @hax i wasn’t asking about philosophy, i was asking about what concrete use cases you have where the author wants to deny you access to something, but you still need runtime access to it, and where forking isn’t a better option? |
This is a false negative. Many members don't care about this proposal and never attend the discussion. Some members think this proposal is broken but don't want to veto it because veto a proposal have a very huge social cost in the committee. (I got this message privately from at least three different TC39 delegates.) So I already said it's a process failure, and I already give my suggestion several months ago: tc39/proposal-class-fields#140 (comment)
And one person you very familiar in TC39 send me a message privately:
@ljharb Let's speak frankly and sincerely, do you really think current process is really fine after you read such messages from TC39 delegates? Do you really think the current proposal is qualified for stage 4 (you already said you strongly prefer [[Set]])? Do you really have confidence that such fuzzy proposal will be the new GOOD part not new BAD part of JS? |
Yes, i still think the current proposal is more than fine, i think it’s as perfect as is possible (with the exception of Set vs Define, but i don’t consider that fatal, just unfortunate). I’m aware that some committee members think it’s appropriate to avoid the “social cost” of vetoing while still quietly undermining proposals as they proceed, but i don’t think that legitimizes their positions. I advocated strongly for Set, and as soon as the decision was made, i stopped doing so - by choosing not to object, imo i waived my right to be upset about the result. Others may not feel the same way about choosing not to object, of course, but that’s how i feel about it. This proposal, and all of its feedback, has been brought up many times in committee, both before stage 3, during the advancement, and during stage 3 - and engines have continued to work towards implementing and shipping it, and the general feeling in the room has seemed to remain “some have reservations; negative feedback is unfortunate; this is still likely to be the best option”. In general, the best design isn’t necessarily going to be something anyone likes, and certainly will never be something everyone likes - it’s going to be something that meets the most constraints while mitigating as much unavoidable damage as it can. To me, class fields meets and greatly clears that bar. |
@ljharb Too unfortunately. I can only say JS is doomed because some guy like you just ignore all the signal of process failure of TC39 and make yourself believe you are doing something right. |
I think there’s places the process needs to improve, but i don’t consider there to be any failures - a process failure would be if any new considerations had come into play “too late”. That’s not the case here. Aspects of every suggested alternative have been long since considered and rejected; the proxy thing isn’t unique to class fields (but is a useful issue to be tackled); and really the only thing that hasn’t gone perfectly is that there’s a small but vocal group of developers who we can’t seem to either convince or please. That’s truly a shame, but we can’t halt language process pending persuasion of everyone with an opionion :-/ |
Too many cases. I just give you the simplest. You need to hack a library for some reason (for example, fix a simple bug but need to access or trap a private method) but this library is a legacy close-source library and what you get is only mangled script files. |
It’s JavaScript. There is no closed-source, and you can run the mangled files through a beautifier sufficiently to be able to make any modifications you need. |
You are saying I'm one of the small and vocal group. But most programmers see my presentation about class fields never agree with you. Ok, you would be the editor of ECMAScript and keep doing what you think correct thing. Fine, maybe it's time for me to say goodbye to JavaScript. |
No. It may pay much more cost to beautify (though there are still meaningless short names) and locate and debug than simply runtime reflection. And it may even illegal to do such thing. |
How could it be illegal? |
I'm thinking it might be a good idea to let this thread rest for now. (Keep in mind that we have a shared goal of making JS as good as it can be.) |
License may never allow reverse engineering and/or modify it without grant. |
@zenparsing Ok. You are right. The difference is, I want make JS as good as it can be, and if the process make this goal fail, I would like to fix it. @ljharb as a delegate of TC39 only want to make JS as good as it can be in current process and never admit there are big possibility the process already failed. Again, not only this proposal, can anyone in TC39 could explain to all programmers STC/PTC issue is not a failure of process? |
There's always a possibility that there could be problems. I'm not "refusing to admit" anything, I just don't agree with your assessment of the problems. PTC is indeed a failure of the old process. The new process would not have permitted PTC to land until it had two shipping browser implementations. However, we're not talking about that - we're talking about class fields. Let's stay on topic. |
Yes, PTC is indeed a failure of the old process, and we still have no process to solve "a failure of the old process", so how can we make sure we will never fail again in current process? And I have given many evidence that current process make a very big burden to at least some delegates to do what they really want to do (veto this proposal, or pursue some process change to solve the problem). If this is not process failure I don't know what is. |
Ok, I also just don't agree your assessment of the problems. So again, how could we have any meaningful communication? Ok, it seems never possible because I already be marked as one of the small and vocal group even most programmers heard my speech never agree with you. But you can also mark all of them as small and vocal group. So how it could be not a small? How many person you need to convince you most js programmers don't like your decision? 500? 1000? 10000? |
Part of the process is supposed to be reviewing potential candidates and choosing the best (or at least the one with the most potential) from among them. New potential candidates have come that provide the same basic feature set without all the complications. However, it seems these candidates (new considerations) have come into play "too late" because too much has been invested in the current stage 3 proposal. How is this not a process failure by your own definition? |
I strongly suspect that if @hax and @rdking were to give one presentation and Rather than continuing to debate that point without definitive evidence, I think it would be more constructive to focus on @hax's criticism of the process. While there are many ways in which the process is working, @hax has pointed out some real problems (however much we may disagree about their severity or agree with some of his points but not others).
"New considerations" here should also include new community feedback about existing issues (not just new technical issues never noticed before) if it could change the balance of previous decisions. A factor in many of those decisions was the committee's understanding of how the decision would affect developers and which issues they care about more than others. The committee's understanding of this at the time it decided to advance the proposal to stage 3 could have been flawed. Obviously the committee can't continue revisiting every issue ad infinitum, but I guess the real question is, what would it take for the committee to reconsider its decision on a particular issue in a stage 3 proposal? Supposedly it's possible for user feedback to cause a proposal to be reconsidered or at least minorly adjusted if it's significant enough, but from what I've seen it seems like there is absolutely no feedback from the community that would be regarded as "significant enough" if it in any way involves information that was known to the committee at the time the decision was made—even if it were causing huge practical problems for thousands of developers on an unexpected scale. (I don't think that's the case for class fields, but I'm saying even if it were it looks like stage 3 would still be irreversible. I hope I'm wrong.) |
We can try.
I can take any order.
Yeah, they will know how some TC39 guys use double standard to reject others which have more solid rationale, but forward themselves which is broken. 🤪
Yes, I use 3 hours only cover half issues of this proposal 😥
After my 3 hours speech, several experienced js programmers (for example, @fengmk2) told me: "I'm shocked, even I already know there may be some issues, I never expect so many serious issues."
Actually I think it's ok to based on voting, if the voters were willing to go to a place in a cold wave, snowy day, listen a 3 hours speech which have no help to their daily work (they were all warned before I started the talk), still do vote even most people were hungry. 😭
The biggest problem is, in current process, you only need one guy, to reject any suggestion, include the suggestion of fixing process. 😳 |
@ljharb, do you seriously think that everybody else will be fine with current proposal? If yes, do you have any evidence for that? |
I think we are all agreed that JS should have instance-level hard privacy in some form, but there are also use cases for reflection, where accessing internal properties/variables is desirable. (This can and perhaps should require explicit permission from the class author to enable reflection.) @rdking has mentioned in various places how a more robust reflection API could be helpful, and I agree. And I think such a reflection API could be helpful in conjunction with class fields as well, so this thread could be the start of a design discussion that would be helpful for either proposal.
FYI, a reflection API was already seriously considered for class fields:
tc39/proposal-class-fields#36 (comment)
Given the "high implementation burden" cited, this should probably be a follow-on proposal but there's no reason we can't start discussing it now.
The other reason I'm starting this thread is to reply to @hax's comment at #10 (comment)
I don't think that's the reason the requests for soft private by default were rejected. I participated in several of those discussions and personally I'm on the fence about this issue...I use libraries far more often than I write and maintain them, and I can definitely see some downsides of requiring library authors to deliberately and individually decorate every private field that should be available for reflection. I and many others expressed our concerns and I never felt that the committee dismissed them lightly (see for example tc39/proposal-class-fields#100 (comment)). I support the class fields proposal despite still being somewhat unconvinced that hard private is the best default. I would also support
private x
syntax that's soft private by default, but apparently that option is unacceptable to some committee members even given an alternative mechanism (such as private symbols) for hard private.The text was updated successfully, but these errors were encountered: