!Re: proposal: Object Members

Michael Theriot michael.lee.theriot at gmail.com
Tue Jul 31 17:17:14 UTC 2018


Should a sealed/frozen object be privately extensible? I don't actually
know, but interesting point.

On Tuesday, July 31, 2018, Ranando King <kingmph at gmail.com> 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 <isiahmeadows at gmail.com>
> 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/23d67ebec901b3471b84e63f58a96b
>> 51a36f3671/lib/jsdom/browser/Window.js#L80
>> [4]: https://github.com/jsdom/jsdom/search?q=_globalProxy
>> [5]: https://github.com/jsdom/jsdom/blob/ad0e551b1b633e07d11f98d7a30287
>> 491958def3/lib/jsdom/living/websockets/WebSocket-impl.js#L49
>>
>> -----
>>
>> Isiah Meadows
>> contact at isiahmeadows.com
>> www.isiahmeadows.com
>>
>>
>> On Tue, Jul 31, 2018 at 1:55 AM, Ranando King <kingmph at gmail.com> 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 <isiahmeadows at gmail.com>
>> > 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
>> >> contact at isiahmeadows.com
>> >> www.isiahmeadows.com
>> >>
>> >>
>> >> On Mon, Jul 30, 2018 at 9:00 PM, Ranando King <kingmph at gmail.com>
>> 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 <kingmph at gmail.com>
>> 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 <
>> isiahmeadows at gmail.com>
>> >> >> 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
>> >> >>> contact at isiahmeadows.com
>> >> >>> www.isiahmeadows.com
>> >> >>>
>> >> >>>
>> >> >>> On Mon, Jul 30, 2018 at 8:04 PM, Ranando King <kingmph at gmail.com>
>> >> >>> 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
>> >> >>> > <isiahmeadows at gmail.com>
>> >> >>> > 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
>> >> >>> >> contact at isiahmeadows.com
>> >> >>> >> www.isiahmeadows.com
>> >> >>> >>
>> >> >>> >>
>> >> >>> >> On Mon, Jul 30, 2018 at 6:35 PM, Waldemar Horwat
>> >> >>> >> <waldemar at google.com>
>> >> >>> >> 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
>> >> >>> >> es-discuss at mozilla.org
>> >> >>> >> https://mail.mozilla.org/listinfo/es-discuss
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20180731/b2aa8da6/attachment-0001.html>


More information about the es-discuss mailing list