zenparsing / proposal-private-symbols

A proposal for private symbols in JavaScript
21 stars 4 forks source link

Isn't WeakMap semantics what we want? #4

Open syg opened 6 years ago

syg commented 6 years ago

The fundamental claim I am surprised by here is that WeakMap semantics isn't what we want after all for per-object private state in JS. WeakMaps were originally part of the reason private symbols didn't make it in the ES6 era, and have since then been used in the community as a way to do per-object private state.

WeakMap semantics is not property semantics, and is definitely confusing if thought of as such. IIUC, a starting point of this proposal is that since class fields look close enough to properties but instead have WeakMap semantics, this is a point of confusion that we should not have. But the starting point in my mind is that not that private state should behave like properties, but that they have WeakMap semantics.

I contend WeakMap semantics remains what we want, so what I'd like to understand better is the following. Extensive discussion around symbols vs private state happened during ES6 and consensus was reached that ultimately, private state should be done via WeakMap semantics, and we have been operating with that since then. What has changed to make symbols the preferred path now?

Cc'ing some other interested parties @littledan @aklein @bakkot @erights

erights commented 6 years ago

I fully agree that weakmaps are the uniquely right model of private state. Nothing else meets the necessary conditions.

zenparsing commented 6 years ago

@syg

I'll be sure to address those topics in the presentation.

[WeakMaps] have since then been used in the community as a way to do per-object private state

I woudn't quite say that we have a cowpath here, but yes, WeakMaps work fine for per-object private state (although there are some performance issues currently). The claim isn't that WeakMaps are inappropriate for per-object data, the claim is that WeakMaps are inappropriate as a basis for private "properties".

WeakMap semantics is not property semantics, and is definitely confusing if thought of as such

Agreed, and the moment that we introduce property syntax for WeakMaps we invite that confusion.

@erights

I don't necessarily disagree with your position, as stated. But that statement does not take into account the fact that WeakMaps are being asked to not only model private state, but to also model things like accessor methods and static methods and property keys (via decorators). The question becomes: are WeakMaps the appropriate mechanism to model these other things?

One wonders whether private symbols are the uniquely right model of private property keys...

Nothing else meets the necessary conditions.

I can guess the conditions that you have in mind, but would you mind formalizing them so that I don't make any wrong assumptions?

erights commented 6 years ago

Nothing else meets the necessary conditions.

I can guess the conditions that you have in mind, but would you mind formalizing them so that I don't make any wrong assumptions?

Membranes must be secure and extremely transparent. I say extremely transparent because they are not---and cannot be given be given other constraints---totally transparent. Existing membrane code must never become less secure or transparent than they are now.

zenparsing commented 6 years ago

I say extremely transparent because they are not---and cannot be given be given other constraints---totally transparent

Thanks - how can I learn more about how existing membranes are not totally transparent?

bathos commented 6 years ago

Has it been considered whether OrdinaryGet, Has, etc could be updated to bail rather than recurse if the key is a private symbol, and to bail for proxy get instead of hitting the target as well? That is, taking away their inheritability? I know some people see the inheritability as a feature, especially with regard to proxies, but I’m not sure if it’s the best angle (it would be useful to me personally, but it is a big shift from WM).

If that is an option people would entertain, would that mean WeakMap semantics have been retained, or are there other things I’m not seeing there?

zenparsing commented 6 years ago

@bathos

One of the attractions of private symbols (as defined here) is that things "just work" with the rest of the language. Take class methods, for instance. I can name a class method using a private symbol:

const sym = Symbol.private();

class C {
  [sym]() { /*...*/ }
}

Like any other class method, the property is stored on the prototype object. And because private symbols do the normal prototype traversal thing, everything just works. The only difference is the encapsulation.

bathos commented 6 years ago

Ah, thanks, that’s a good point. I’ve gotten used to calling out to unexposed functions from classes with WM state — sort of equivalent to "private methods" — so I wasn’t looking at it from a prototype-utility standpoint, just an instance-associated-state standpoint, though I agree that’d be nice to have.

I do prefer the inherited, ordinary property model, but if inheritability is a dealbreaker for others, I would still consider non-inherited private symbol properties superior to the current private fields proposal, hence the question.

Jamesernator commented 6 years ago

Do we actually need the Proxy forwarding rather than just adding the private symbol directly onto the Proxy object itself?

bathos commented 6 years ago

@Jamesernator that is what I do presently (with WeakMaps, I mean — the proxy is the key, not the target). Realistic scenarios where this isn’t an option may exist, but I’m scratching my head a bit trying to envision one.

zenparsing commented 6 years ago

@Jamesernator

From an object-model point of view, proxies don't really "have" properties - they have exotic behavior which can emulate properties (provided they are "consistent" with the properties of the target object). Conceptual issues aside, what would be the advantage of storing properties "on the proxy" as opposed to the target?

Jamesernator commented 6 years ago

It seems like that storing them on the Proxy itself would behave more like the WeakMap-style membrane @erights is describing with just the extra property that foo[privateField] also looks up the field on the prototype chain as well (unlike the current proposal).

zenparsing commented 6 years ago

@erights

Existing membrane code must never become less secure or transparent than they are now.

Are you aware of any current membrane code that does not use the shadow target technique?

erights commented 6 years ago

No. All use the shadow target technique.

zenparsing commented 6 years ago

@erights

Thanks. I've tried to approach the security angle a bit more formally here, but it is by no means complete.

Regarding transparency, it is certainly true that membranes cannot be transparent with respect to private symbols. When passed between graphs, private symbols act similar to an "expando" distortion for those property names. This behavior does not seem particularly unreasonable to me, especially since the primary use case for private symbols is for hiding implementation details within a module or library. Functions are a better mechanism for passing around authority between public interfaces.