!Re: proposal: Object Members
Isiah Meadows
isiahmeadows at gmail.com
Tue Jul 31 21:09:52 UTC 2018
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
contact at isiahmeadows.com
www.isiahmeadows.com
On Tue, Jul 31, 2018 at 1:17 PM, Michael Theriot
<michael.lee.theriot at gmail.com> wrote:
> 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/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
>>> 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
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
More information about the es-discuss
mailing list