If you look at my proposal, it would be a "no" for the same reasons you can't add normal properties to them. -----
Isiah Meadows [email protected] www.isiahmeadows.com On Tue, Jul 31, 2018 at 1:17 PM, Michael Theriot <[email protected]> wrote: > Should a sealed/frozen object be privately extensible? I don't actually > know, but interesting point. > > > On Tuesday, July 31, 2018, Ranando King <[email protected]> wrote: >> >> > Consider what people often use public symbols for now. >> >> I know that I use them as fixed-value unique keys (module globals) for >> properties on objects that I export and don't want others to be aware of or >> able to touch. >> >> > For example, consider this library [1]. In this case, they use a public >> > symbol for their stuff in this file [2]. >> >> And as I said before, if someone passes a non-extensible object to this >> library, it breaks. Since any library can request any object be sealed or >> frozen, the implementation of this library is too fragile. >> >> > Because here, it's not a cache, but it's literally extra associated data >> > in the object. And also, in that case, you *want* the engine to see it as a >> > property, since it can employ relevant IC caching for it. >> >> Here's a parallel for you. Right now, Google has a database with >> information about me on it. I don't have access to that information (module >> privacy) and that information isn't stored anywhere on me or my devices >> (module locality). This is a proper conceptual model. The information Google >> is keeping about me is information they generated. Why should I have to pay >> to store their information? Thankfully I don't. However, this is precisely >> what you're claiming to be a good use case. You want module privacy without >> module locality. If I were to play at a Kevin Gibbons-like response, I'd say >> you've identified the common pattern, but that pattern itself is a bad use >> case, and the use of private symbols as you have defined them doesn't do >> anything to correct the technical issue. Since you cannot stick new >> properties onto a non-extensible object, even private symbols won't solve >> the problem with your use case. >> >> > No, I'm not. I'm drawing a distinction between a pure many-to-one >> > association (weak maps) and a "has a" relationship (private symbol >> > properties). >> >> First, for any given property bag, the keys will need to be unique, but >> that doesn't force uniqueness onto the values. As such, even properties on >> an object provided by your "private Symbol" would still be many-1. When a >> 3rd party library wants to keep information associated with an arbitrary >> object, there are only 3 choices: >> * try to store that information on the object >> * this is what you're advocating, but it's not a good pattern. It's too >> fragile, being subject to break if the incoming object is not extensible. >> * store the information as being associated to the object (WeakMap) >> * this is the pattern that works in all cases (but the syntax is >> cumbersome and the implementation somewhat slow) >> * return a wrapper containing the original object and the new information >> (Proxy or custom wrapper) >> * this is another possibility, but requires that any users accept and >> use the new Proxy or wrapper object in lieu of the original. >> >> > Another scenario is for JSDOM's `Window` implementation, where they have >> > a few underscore-private variables like this [3]. That particular variable >> > is used in several disparate parts throughout the code base [4], but is >> > still conceptually a property. This is a case where a private symbol >> > property is appropriate. >> >> It's not just "conceptually" a property. It's logically a property. Why? >> Because all the objects that it exists on were constructed somewhere within >> the JSDOM library. That's me putting _my_ keys in _my_ pocket. There's >> absolutely nothing wrong with that. >> >> > Conversely in this JSDOM file [5], it's just associating data with an >> > arbitrary object it happens to have, and so using the weak map makes >> > perfect >> > sense. >> >> Conceptually speaking, this is the same scenario as SymbolTree. In both >> cases, the library is generating information associated with an object it >> doesn't own and didn't create. >> >> > BTW, you could make a similar argument against superclass private fields >> > - it's like hiding valuable info in your wallet before you receive it for >> > the first time, but even after dismantling it, you can't find any evidence >> > of that valuable info. >> >> That dog don't hunt. The difference here is that in your use cases, >> library A is "sneakily" storing information on object B. In the case of >> superclass private fields, subclass B has "volunteered" to take on the >> information and functionality of class A. You've essentially compared apples >> and asteroids just because they both begin with "a". >> >> On Tue, Jul 31, 2018 at 2:15 AM Isiah Meadows <[email protected]> >> wrote: >>> >>> > Isn't this precisely what WeakMaps are for? If the data is >>> > "module-internal", then the module needs to be the owner of the data >>> > store. >>> > If the data is about "arbitrary objects" (object from outside the >>> > module?) >>> > then those objects are the keys to the data store. If any object is >>> > thrown >>> > away, the associated data is no longer needed. If this doesn't fit the >>> > functionality of a WeakMap, I don't know what will. >>> >>> Consider what people often use public symbols for now. For example, >>> consider this library [1]. In this case, they use a public symbol for >>> their stuff in this file [2]. >>> >>> But here's the thing: that doesn't really need discoverable, and is a >>> pure implementation detail. Wouldn't it make more sense for them to >>> just use a private symbol instead? Because here, it's not a cache, but >>> it's literally extra associated data in the object. And also, in that >>> case, you *want* the engine to see it as a property, since it can >>> employ relevant IC caching for it. >>> >>> > Isn't that precisely what your question calls for? You're caching >>> > module-internal data about external objects. >>> >>> No, I'm not. I'm drawing a distinction between a pure many-to-one >>> association (weak maps) and a "has a" relationship (private symbol >>> properties). You *could* implement one in terms of the other, but >>> these two types of relationships are *completely* different at a >>> conceptual level and how you model them. >>> >>> For js-symbol-tree, it's not simply associating a node to a value, but >>> setting up the object so it *has* the data required for a doubly >>> linked list tree node. Because this symbol is repeatedly accessed, >>> it's not caching so much as it's adding data the object needs for it >>> to do what it needs to do. >>> >>> Another scenario is for JSDOM's `Window` implementation, where they >>> have a few underscore-private variables like this [3]. That particular >>> variable is used in several disparate parts throughout the code base >>> [4], but is still conceptually a property. This is a case where a >>> private symbol property is appropriate. >>> >>> Conversely in this JSDOM file [5], it's just associating data with an >>> arbitrary object it happens to have, and so using the weak map makes >>> perfect sense. >>> >>> > Likewise, I'm specifically against the abuse of objects to store state >>> > unrelated to the factory that created it. To me, that's as if I came to >>> > visit you and somehow you managed to hide some valuable info in my >>> > wallet >>> > without me noticing, and even if I completely dismantle my wallet, I >>> > wouldn't be able to find it. But somehow you can easily retrieve it the >>> > next >>> > time I come around. That's just conceptually weird. >>> >>> All of the examples here I've presented are for scenarios where the >>> state *is* related to the factory that created the objects. It's not >>> *directly* related (and thus encapsulation is warranted), but it's >>> still *related*, enough so that you usually see the state initialized >>> within the creator's constructor call. It's about as related as the >>> superclass is to a subclass of it. >>> >>> BTW, you could make a similar argument against superclass private >>> fields - it's like hiding valuable info in your wallet before you >>> receive it for the first time, but even after dismantling it, you >>> can't find any evidence of that valuable info. >>> >>> [1]: https://github.com/jsdom/js-symbol-tree >>> [2]: >>> https://github.com/jsdom/js-symbol-tree/blob/master/lib/SymbolTree.js#L28 >>> [3]: >>> https://github.com/jsdom/jsdom/blob/23d67ebec901b3471b84e63f58a96b51a36f3671/lib/jsdom/browser/Window.js#L80 >>> [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy >>> [5]: >>> https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49 >>> >>> ----- >>> >>> Isiah Meadows >>> [email protected] >>> www.isiahmeadows.com >>> >>> >>> On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <[email protected]> wrote: >>> >> One last thing: how would you hope to deal with module-internal data >>> >> stored on arbitrary objects, using any means other than private >>> >> symbols or >>> >> something similar? >>> > >>> > Isn't this precisely what WeakMaps are for? If the data is >>> > "module-internal", then the module needs to be the owner of the data >>> > store. >>> > If the data is about "arbitrary objects" (object from outside the >>> > module?) >>> > then those objects are the keys to the data store. If any object is >>> > thrown >>> > away, the associated data is no longer needed. If this doesn't fit the >>> > functionality of a WeakMap, I don't know what will. >>> > >>> >> Weak maps make sense when the weak map is the dictionary conceptually >>> >> (think: caching). >>> > >>> > Isn't that precisely what your question calls for? You're caching >>> > module-internal data about external objects. >>> > >>> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >>> >> private state that's conceptually (in an abstract sense, not runtime) >>> >> part >>> >> of an object. >>> > >>> > Likewise, I'm specifically against the abuse of objects to store state >>> > unrelated to the factory that created it. To me, that's as if I came to >>> > visit you and somehow you managed to hide some valuable info in my >>> > wallet >>> > without me noticing, and even if I completely dismantle my wallet, I >>> > wouldn't be able to find it. But somehow you can easily retrieve it the >>> > next >>> > time I come around. That's just conceptually weird. >>> > >>> > On Mon, Jul 30, 2018 at 9:42 PM Isiah Meadows <[email protected]> >>> > wrote: >>> >> >>> >> The reason private symbols are appropriate for Node's use case is >>> >> because it's conceptually a mixin, not a simple key/value map with >>> >> various utility functions (and weak map lookup is slower than property >>> >> access). JSDOM uses a similar utility [1] as a sort of mixin. >>> >> >>> >> Keep in mind, I'm specifically *against* the abuse of weak maps for >>> >> private state that's conceptually (in an abstract sense, not runtime) >>> >> part of an object. Weak maps make sense when the weak map is the >>> >> dictionary conceptually (think: caching). But if conceptually, the >>> >> object is the dictionary, putting it in a weak map is giving the >>> >> engine the wrong info - properties have inline caches and heavy >>> >> optimization, but you can't do the same for weak maps in the other >>> >> direction without literally implementing them as properties. (I would >>> >> *love* to be proven wrong here, BTW.) >>> >> >>> >> Let me draw a quick comparison: When do you use a map/set with string >>> >> keys, and when do you use an object instead? >>> >> >>> >> - Both are functionally equivalent, but engines use *very* different >>> >> algorithms for each one. >>> >> - I can almost guarantee you don't use maps when object properties >>> >> work. >>> >> >>> >> One last thing: how would you hope to deal with module-internal data >>> >> stored on arbitrary objects, using any means other than private >>> >> symbols or something similar? To clarify, I'm talking of opaque object >>> >> structs [2], not simply classes. (BTW, that one is easier to manage as >>> >> a struct rather than a class, because of how many "methods" there are >>> >> operating on the state.) >>> >> >>> >> [1]: https://github.com/jsdom/js-symbol-tree >>> >> [2]: https://github.com/isiahmeadows/enigma/blob/master/src/parser.ts >>> >> >>> >> ----- >>> >> >>> >> Isiah Meadows >>> >> [email protected] >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <[email protected]> >>> >> wrote: >>> >> > I meant to say if the object passed to the 3rd party function..... >>> >> > >>> >> > >>> >> > On Mon, Jul 30, 2018 at 7:59 PM Ranando King <[email protected]> >>> >> > wrote: >>> >> >> >>> >> >> Just that use case alone is problematic. If the 3rd party function >>> >> >> is >>> >> >> not >>> >> >> extensible, then the new private data should not be allowed. If the >>> >> >> library >>> >> >> cannot function without storing that data, then the function will >>> >> >> have >>> >> >> no >>> >> >> choice but to fall back to WeakMaps which don't care if the key is >>> >> >> not >>> >> >> extensible. So why not just stick with WeakMaps for that case? And >>> >> >> if >>> >> >> that's >>> >> >> the case, then there would be little need for so open a means of >>> >> >> defining >>> >> >> private field names. The proposal I'm offering offers the room to >>> >> >> extend it >>> >> >> in the future to support everything else you might look for from >>> >> >> your >>> >> >> private symbols idea.... unless you think I missed something. >>> >> >> >>> >> >> On Mon, Jul 30, 2018 at 7:26 PM Isiah Meadows >>> >> >> <[email protected]> >>> >> >> wrote: >>> >> >>> >>> >> >>> That is one supported use case, yes. But that isn't the only use >>> >> >>> case >>> >> >>> this supports. It can still extend to traditional private class >>> >> >>> data, >>> >> >>> too. >>> >> >>> >>> >> >>> ----- >>> >> >>> >>> >> >>> Isiah Meadows >>> >> >>> [email protected] >>> >> >>> www.isiahmeadows.com >>> >> >>> >>> >> >>> >>> >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <[email protected]> >>> >> >>> wrote: >>> >> >>> > So you're wanting the ability for a 3rd-party function to be >>> >> >>> > able to >>> >> >>> > store >>> >> >>> > data private to that library on an object it didn't create, and >>> >> >>> > that >>> >> >>> > only >>> >> >>> > that library can access? >>> >> >>> > >>> >> >>> > On Mon, Jul 30, 2018 at 6:36 PM Isiah Meadows >>> >> >>> > <[email protected]> >>> >> >>> > wrote: >>> >> >>> >> >>> >> >>> >> First, my private symbols are properly *private*. The only >>> >> >>> >> "unexpected" thing that could happen is making an object larger >>> >> >>> >> memory-wise, which engines already have to be equipped to >>> >> >>> >> handle >>> >> >>> >> now >>> >> >>> >> (libraries aren't always well-behaved, and like to occasionally >>> >> >>> >> add >>> >> >>> >> expando properties to builtins and DOM elements). About the >>> >> >>> >> only >>> >> >>> >> thing >>> >> >>> >> most people would care about is in the debugger. >>> >> >>> >> >>> >> >>> >> Second, I had things like this in mind with supporting expando >>> >> >>> >> properties: >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> https://github.com/nodejs/node/blob/ae4fde8bc883686def5badfb324236320669e8f4/lib/internal/linkedlist.js >>> >> >>> >> >>> >> >>> >> In that case, the Node.js people made it a pseudo-mixin rather >>> >> >>> >> than >>> >> >>> >> an >>> >> >>> >> actual type for performance reasons - there's fewer object >>> >> >>> >> allocations >>> >> >>> >> and they needed that. >>> >> >>> >> >>> >> >>> >> So I've considered the expando problem, and I disagree about it >>> >> >>> >> being >>> >> >>> >> a problem at all. >>> >> >>> >> >>> >> >>> >> ----- >>> >> >>> >> >>> >> >>> >> Isiah Meadows >>> >> >>> >> [email protected] >>> >> >>> >> www.isiahmeadows.com >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat >>> >> >>> >> <[email protected]> >>> >> >>> >> wrote: >>> >> >>> >> > On 07/29/2018 04:37 PM, Isiah Meadows wrote: >>> >> >>> >> >> >>> >> >>> >> >> BTW, I came up with an alternate proposal for privacy >>> >> >>> >> >> altogether: >>> >> >>> >> >> https://github.com/tc39/proposal-class-fields/issues/115 >>> >> >>> >> >> >>> >> >>> >> >> TL;DR: private symbols that proxies can't see and that can't >>> >> >>> >> >> be >>> >> >>> >> >> enumerated. >>> >> >>> >> > >>> >> >>> >> > >>> >> >>> >> > Aside from syntax, the main semantic difference I see between >>> >> >>> >> > this >>> >> >>> >> > alternative and the main one is that this alternative defines >>> >> >>> >> > private >>> >> >>> >> > fields >>> >> >>> >> > as expandos, creating opportunities for mischief by attaching >>> >> >>> >> > them >>> >> >>> >> > to >>> >> >>> >> > unexpected objects. Aside from privacy, one of the things >>> >> >>> >> > the >>> >> >>> >> > private >>> >> >>> >> > fields proposal gives you is consistency among multiple >>> >> >>> >> > private >>> >> >>> >> > fields >>> >> >>> >> > on >>> >> >>> >> > the same object. In the rare cases where you don't want >>> >> >>> >> > that, >>> >> >>> >> > you >>> >> >>> >> > could >>> >> >>> >> > use >>> >> >>> >> > weak maps. >>> >> >>> >> > >>> >> >>> >> > Waldemar >>> >> >>> >> _______________________________________________ >>> >> >>> >> es-discuss mailing list >>> >> >>> >> [email protected] >>> >> >>> >> https://mail.mozilla.org/listinfo/es-discuss > > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

