-
Notifications
You must be signed in to change notification settings - Fork 113
Move to user/library space with decorators? #16
Comments
@claytongulick, have you seen the FAQ for private fields? In particular, Why isn't access this.x? addresses a wide variety of proposals for other syntax, including I think this one. I'm not sure I understand what you mean about |
@bakkot thanks for pointing me to that article, no I hadn't see it yet and it raises some great points. I'm not sure I'm convinced that using decorators as a more generic approach to access modifiers is the wrong approach though. Thinking about the current proposal, it seems like JavaScript is playing catch-up to other languages for feature parity with classes. Walking back from this and using a generic approach like decorators, I think, makes JS more advanced than other languages - we're not limited to existing concepts of access modifiers. The best argument I saw in that FAQ against a run-time approach like decorators is the performance impact. That's non-trivial, and we'd be killing any ability for the compiler to do it's job with privates. Also, it'd be a run-time error rather than a syntax error as defined in the current proposal, so both of those are negatives for sure. The question is whether the power of the decorator approach outweighs those negatives? What I meant about function/arguments.caller is that in order to determine whether calling code should be allowed to access a property/method, you need to know something about the calling code - i.e. Is this function a member of my class? Is it defined elsewhere? Is it a member of a subclass? Or maybe a class that I'd like to consider a friend? For the decorator approach to work, you'd need to be able to figure out who's accessing the method/property, and what their context is. Maybe instead of beefing up Function.caller, it'd make more sense to add a param to the decorator signature that would take a caller_info data structure of some type? |
I think I agree with @claytongulick. JavasScript is very powerful today because of this built in flexibility. Adopting a more restrictive system just because it is what other languages are doing would in my opinion be a step backward. |
I think there is merit here. Doing class SimpleThing {
#x = 0;
y = 1;
doSomething(change_x) {
this.#x = change_x + this.#x + this.y;
}
} In contrast the above proposal could meet the goal of private variables in classes without forever limiting how and to whom variable access is given in the future. Consider this: class AnotherSimpleThing {
@Private
x = 0;
@Protected
y = 1;
doSomething(change_x) {
this.x = change_x + this.x + this.y;
}
} In my view it is a simpler solution to just make anything decorated with Composition problems aside, Using decorators opens the future possibility to define access in very granular ways. I understand and acknowledge the problems with this approach as outlined in the linked article. However, creating a solution that has the outlined composition problems isn't a great idea either. I think what @claytongulick has proposed is worth consideration. |
They can still have |
While it's fun to speculate about alternate approaches, the FAQ entry on Why isn't access Absent a static type system, any proposal which allows @blargism, I'm not sure I understand your point about composition. Can you clarify? Edit to add:
Sure. But the main reason this matters is when the fields are split across a superclass and subclass: that is, a superclass has a field |
It seems pretty clear that These objections have been discussed in many threads on the private state repo; I'm not sure what new ground there is to cover. I believe |
@littledan I know I came to the conversation late, and I apologize for that - I've been watching other language features a lot more closely and for some reason this one was under my radar. I know you've done a ton of work on this feature and it's incredibly well thought out, and well discussed. I suppose I'm just trying to make sure that no stone has been left unturned, since there seems to be consensus about the distastefulness of '#'. I'm still scratching my head about your comment on how I think the whole basis of my thoughts around So, that you'd intercept the private property with a decorator and prevent it from actually being added to the class, then use a Proxy to observe access to the property. If we had caller info and context, whether that be via an addition to the decorator spec or a beefed up Function.caller, we could check to see if the function that's accessing the private is a member of the same class. I must be missing how this wouldn't satisfy the points you laid out in https://github.com/tc39/proposal-private-fields/blob/master/FAQ.md#why-isnt-access-thisx . Performance wise, of course, this would be horrendous - so that's a really strong argument against the decorator concept - however, we already do all sorts of tricks in tight code to work around quirks in js performance, like bringing a closure variable into the local scope to prevent scope chain navigation. High performance loops accessing a private seem to be less of a use case to me than the ability to have a more generic system for access modifiers. I really like your point about decorators providing an escape hatch for privates, and I agree - they totally do. It does sort of reinforce in my mind that access modifiers are a meta programming construct though. Does is make sense to have two ways of defining access? One baked into the language and one in user/library space? That seems like something that we'll take a knock on from folks coming from other languages. Also, thanks to you and @bakkot for taking the time to rehash all this again, you've done a ton of great work here and it's really cool of you to take the time to review this. Beers are on me if you're ever in DFW. |
@claytongulick Ah, I understand your point about Separately, I think we really don't want to add |
@bakkot right, the entire concept hinges on the ability to get the caller info, perhaps arguments.caller isn't the best way, but what would you think about expanding the decorator signature to pass in a caller_info structure? Also, I agree on the performance problem and how unreasonable it is, but I guess my thought is that with the decorator approach, we give the power and decision making ability to the user/library for balancing performance v/s protection. For example, for someone who doesn't care about super strict checking and potential leaking, there could be a @fast_private - one that's "good enough" but doesn't handle all the cases you mentioned. |
A nice thing about |
@claytongulick For this to work, proxy traps would need to know something about the code which invoked the trap: in particular, they'd need to know... all of the classes syntactically containing that code, I guess, and maybe something about Re: performance vs protection: the approach currently in the proposal gets both, at the cost of configurability. But the impression I get is that private fields are overwhelmingly the majority use case for language-supported access modifiers, so I don't know we ought to sacrifice either performance or protection to achieve more configurability. |
@bakkot that's a head-scratcher for sure, but a good problem - I think. It's the same problem that arguments.caller and Function.caller were trying to solve, but to @littledan 's point, fell down on. Solving the "who called me, and how?" question via Proxys is a really interesting concept, that I hadn't thought of. Until now I was thinking in terms of possibly expanding decorators to pass in a caller_info structure of some sort but with your Proxy approach it solves the problem a lot more generically. Would it be necessary to pass more than the |
@claytongulick I think @bakkot was explaining something that's rather the opposite--it's intractable to square using ordinary property keys for private state because it would require getting weird context information, which is probably a bad idea to provide.
Unfortunately, I think it's time to do that--it's been explained here and elsewhere (e.g., the FAQ) why this would be infeasible. |
@littledan I'm surprised that you closed this issue given the volume of feedback you've received on this feature over the past year. I'm pretty far from the first person who's raised an objection to changing the language as proposed here. @bakkot was indeed conjecturing on why he thought passing caller context via Proxy traps would be difficult, and raised fair points about performance implications, which I agree with and address. Difficult doesn't mean impossible, and I think the idea of using Proxys to solve the problem that arguments.caller and Function.caller are clearly trying to address is an interesting notion. I haven't seen anything in the FAQ or your other articles that would contradict the use of decorators as access modifiers at all, quite the opposite - they tend to reinforce the point. Whether or not decorators end up being the correct solution for access modification, I'm confused why we're pushing forward with a proposal that's received voluminous negative feedback, consumes one of our only "free" characters, and doesn't completely solve the access modifier problem - i.e. it just punts on questions about future modifiers like While I'm sure it's exhausting to have yet another objection raised at Stage 2, it's premature to close down discussion on this issue and proposal. |
@claytongulick, to be clear, I think the issues I raise above are fatal. The FAQ doesn't address the question of how decorators as spec'd could be used to implement private fields because they cannot, as far as I'm aware. If you have a coherent design in mind for what that might look like, we could discuss it further. Though also I don't think decorators are the right approach for private fields, given the strength of the encapsulation that private fields are intended to provide. (Also, by bringing up proxy traps I thought I was addressing your proposal, since decorators are executed once at class instantiation, whereas access can be done at a much later point - that is, decorators do not run at the appropriate time to decide if external code should have access to a field: "exposing caller info to decorators" is not possible. For something like what you propose to work we would have to expose it to proxy traps: which, like I say, I don't see any way to do that reasonably, and separately I think we should not.) |
@bakkot I was afraid that'd be the case with expanding the signature of decorators, I wasn't really sure how it would work to pass a caller_info structure to those shy of replicating Proxy type functionality via a callback - so yeah, that leaves Proxy traps, or xxxx.caller. Honestly, I'm more intrigued by the idea that @lifaon74 mentioned in this issue about adding modifiers to the descriptor. I haven't dug deeply into it yet to bang it up against your and @littledan 's points in the FAQ and elsewhere, but on the surface it's an exciting possibility, sort of a middle course between a pure decorator approach and the new I do think there could be merit in the idea of expanding Proxy to include caller info. I'm going to noodle on that a bit and perhaps draw up a proposal if it makes sense, but I think that's an independent issue. I think that @lifaon74 's approach makes more sense than my proposal for a pure decorator approach with runtime traps, so I think I'll follow that thread to see where it goes. Thanks again for your time and attention and thoughtful responses here! |
I wonder if it would make sense to have class access modifiers be a userspace feature via decorators rather than a defined syntax.
In way, access modifiers really are a meta programming construct. So it would sort of make sense to use the upcoming decorator syntax for implementation, also it wouldn't limit class access to "whack a mole" additions so that JS can be like other languages. Currently we're talking about private and public, but how long will it be until there's pressure for 'protected'? After that, how long until something like c++ 'friend' is proposed? If access modifiers are handled via decorators instead, this becomes a library issue.
So, '
@private
', '@protected
', etc... would do what you expect, and might be added to the core-decorator library.Additionally, I could see fancy new concepts emerging for wild access modifiers like @class_signed_by("[public key]") that might limit access to a class written by an authorized developer.
In order to make the decorator concept work, I think there's a need to tighten up Function.caller a bit, or resurrect arguments.caller - they'd have to be expanded a bit to include the context of the calling function, or perhaps an addition like Function.class. Are there security concerns with this?
The text was updated successfully, but these errors were encountered: