18:20
<danielrosenwasser>
https://1drv.ms/b/s!AltPy8G9ZDJdq3JSrN6Dh1XYVwpW
22:08
<Michael Ficarra>
jschoi: you could start by proposing a SameValueSet and SameValueMap, though I doubt it would be too popular
22:09
<Michael Ficarra>
I continue to believe that these data structures should've used SameValue, and this is a perfect example of why
22:10
<Michael Ficarra>
you could easily have the current behaviour by replacing -0 with 0 before doing any map operations, but you can't easily go the other way around
22:11
<Mathieu Hofman>
We you can easily replace with a unique symbol
22:11
<Mathieu Hofman>
That's what I've done when I needed to support -0 in Map/Set
22:11
<Michael Ficarra>
as long as that symbol doesn't escape, which you can't guarantee when the map is user-provided
22:12
<Mathieu Hofman>
if the map is user provided, sure
22:12
<Mathieu Hofman>
I'm talking in the case of wrapping a map
22:12
<Michael Ficarra>
also jschoi has use cases in mind like the user priming the cache, which again wouldn't work with a special replacement symbol
22:12
<Mathieu Hofman>
which as we agreed was the only way to preserve that kind of invariants
22:13
<Michael Ficarra>
yeah I used the strategy of wrapping two sets in https://www.npmjs.com/package/samevalueset, but a symbol probably would've worked fine there
22:15
<Mathieu Hofman>
That said, I'd love if we could have a comparison predicate configurable when building the map, but to no shoot down performance, we may need to expose SameValueZero as a predicate the same way SameValue is exposed as Object.is, so that not providing a comparison predicate isn't magical.
22:17
<Michael Ficarra>
const SameValueZero = (a, b) => a === 0 && b === 0 || Object.is(a, b);
22:19
<Michael Ficarra>
are you saying that passing that in would be too slow because it's not built-in?
22:21
<bakkot>
passing it in fundamentally does not work because that means you have to do a comparison against every element
22:21
<bakkot>
you need a user-defined hash function as well; just having user-defined equality is not useful
22:21
<Mathieu Hofman>
Why so complicated? const SameValueZero = (a, b) => a === b, but the point is that this wouldn't be optimizable as the implementation has to call the user predicate instead or recognizing it as its own
22:22
<nicolo-ribaudo>
NaN
22:22
<Michael Ficarra>
not sure if trolling...
22:23
<Mathieu Hofman>
Oh right, ugh I'm tired
22:23
<Michael Ficarra>
we all are after this week
22:23
<Mathieu Hofman>
Ok nvm forget all of the above
22:28
<Mathieu Hofman>
yeah in that case the only other alternative I can think of if would be providing mapping functions that transform keys before insertion, probably with a SameValue based caching mechanism to be robust against instability of the transform function if the initial key is already known
22:29
<Mathieu Hofman>
which seems not very efficient
22:30
<Ashley Claymore>
yeah I used the strategy of wrapping two sets in https://www.npmjs.com/package/samevalueset, but a symbol probably would've worked fine there
At first I thought the second set was just to store the -0, then checked the code. Using two sets to also preserve the insertion order is a classy touch!
22:32
<ljharb>
efficiency is nice, but the capability is more important
22:32
<ljharb>
what's the typical range of map/set content counts?
22:33
<ljharb>
are many people making maps and sets with a million+ things, in a hot path, and who also want this extra capability?
22:40
<Kris Kowal>
I think tens of thousands is a pretty reasonable estimate for a production-sized Map in some Node.js applications.
22:43
<Michael Ficarra>
if they're backing their memoisation implementation with it, it could see a lot of content
22:44
<ljharb>
and would any of these use cases (which obviously exist; i'm asking about %) need any of the functionality from these hooks?
22:44
<ljharb>
because if so, they're probably already paying the perf cost, and if not, they presumably wouldn't pay it (we wouldn't want to add a feature that penalized existing code paths, obv)
22:45
<bakkot>
I think a lot of people would be very surprised if using a hook made set membership O(n) instead of ~constant
22:46
<bakkot>
you don't need large sets for that to be a problem, you just need to be doing lots of queries
22:46
<bakkot>
like if you don't want fast membership tests, you can just use a list and .find. which is what this would amount to anyway
22:48
<jschoi>
jschoi: you could start by proposing a SameValueSet and SameValueMap, though I doubt it would be too popular
Distinguishing -0 is probably uncommon enough that we can punt doing so to a userland Map-like class. But at least it would be possible, if memo takes a Map-like cache argument. I’m already going to propose LRUMap and friends.
22:48
<shu>
efficiency is nice, but the capability is more important
if that is the case just write your own set?
22:48
<shu>
or map, i guess this conversation is about map
22:48
<ljharb>
if that is the case just write your own set?
that's what folks already are doing. but it's not ergonomic to do so, and, there's a bunch of other downsides including that your invariants are hard to enforce
22:49
<Michael Ficarra>
jschoi: I guess? It's a design worth considering, but it feels a bit icky for default memoise to do any coalescing.
22:49
<bakkot>
i am very, very strongly opposed to having hooks which cause set membership querying to be O(n)
22:49
<bakkot>
we just... we cannot do that
22:49
<bakkot>
the whole point of the data structure is to have Not That
22:50
<shu>
ergonomics is a bad reason to slow down cases that do require performance
22:50
<jschoi>
jschoi: I guess? It's a design worth considering, but it feels a bit icky for default memoise to do any coalescing.
Do you think a EquivalenceMap(mapLike, equivalenceFn) built-in would be useful?
22:50
<Michael Ficarra>
jschoi: no, for the reasons we're talking about above
22:50
<shu>
i'm slowly being radicalized into something more extreme, which is that ergonomics is a bad argument
22:52
<ljharb>
i am very, very strongly opposed to having hooks which cause set membership querying to be O(n)
i mean, i would hope that's not the case, and i think we could design something where it wouldn't have to be
22:52
<shu>
i am very, very strongly opposed to having hooks which cause set membership querying to be O(n)
i am also strongly opposed
22:52
<ljharb>
for example, instead of a "predicate" hook, a "comparison value" hook, that by default is x => x but could be made to return a string, or a unique symbol, or something
22:52
<ljharb>
i assume that would have potential memory costs but little to no lookup costs
22:53
<Kris Kowal>
My position is approximately that more interesting Maps and Sets can be created in user code by composition, without inheritance, using what we already have, and that record and tuple keys will just make that easier.
22:54
<Mathieu Hofman>
Right that's what I suggested earlier, but to prevent bugs due to instability of the mapping function, you have to cache its results
22:54
<ljharb>
My position is approximately that more interesting Maps and Sets can be created in user code by composition, without inheritance, using what we already have, and that record and tuple keys will just make that easier.
yes but Map.prototype.set.call will continue to not work properly on these userland maps
22:54
<bakkot>
sure they will
22:54
<bakkot>
well
22:54
<bakkot>
it will throw
22:54
<ljharb>
Right that's what I suggested earlier, but to prevent bugs due to instability of the mapping function, you have to cache its results
yes, in that example the hook is only ever called once per key
22:54
<bakkot>
it won't violate any invariants
22:54
<bakkot>
so, seems fine
22:54
<shu>
why do you want built-in map methods to work for your userland maps?
22:55
<ljharb>
the same reason i want built-in iteration syntax to work for userland iterables?
22:55
<Kris Kowal>
why do you want built-in map methods to work for your userland maps?
I certainly have no such expectation.
22:55
<bakkot>
those are... very... different
22:55
<shu>
the same reason i want built-in iteration syntax to work for userland iterables?
lol i am leaving this convo
22:55
<ljharb>
to me they're all conceptual protocols
22:55
<ljharb>
i want to be able to work with map-like collections in a robust fashion
22:55
<bakkot>
just call .set
22:55
<bakkot>
that is how you work with map-likes
22:56
<Mathieu Hofman>
The protocol in this case is .get, .set, .delete ?
22:56
<ljharb>
lol i can see none of you are going to react well to my preferences here
22:56
<Kris Kowal>
I mean, I love protocols.
22:56
<ljharb>
in general, i want to minimize how much i trust other code.
22:56
<ljharb>
"just call .foo on the argument" is not doing that
22:57
<jschoi>
My position is approximately that more interesting Maps and Sets can be created in user code by composition, without inheritance, using what we already have, and that record and tuple keys will just make that easier.

Speaking of tuples, has there been any recent activity around composite keys (which are kind of an alternative to tuple keys)? I ask because someone has suggested them for https://github.com/js-choi/proposal-function-memo/issues/4#issuecomment-1083552333, and I don’t see any activity around them since 2019.

The alternative for memo would be to use argument tuples as keys in the Map-like cache and to replace objects in the tuple with symbols from a WeakMap.

22:58
<ljharb>
lol i am leaving this convo
right but see this kind of reaction to the things i find important encourages me to react equally strongly to things like structs and atomics.
22:58
<shu>
excuse me?
22:58
<Mathieu Hofman>
Unfortunately I hear there is push back against considering tuples of unique symbols as WeakMap keys ...
22:58
<ljharb>
because i don't value any of the motivations for either of those, personally, but that should mean i stay out of the conversation, not that i ridicule or block them
22:59
<Kris Kowal>

Speaking of tuples, has there been any recent activity around composite keys (which are kind of an alternative to tuple keys)? I ask because someone has suggested them for https://github.com/js-choi/proposal-function-memo/issues/4#issuecomment-1083552333, and I don’t see any activity around them since 2019.

The alternative for memo would be to use argument tuples as keys in the Map-like cache and to replace objects in the tuple with symbols from a WeakMap.

We have certainly had conversations about records and tuples as recently as last December in the SES calls https://docs.google.com/document/d/1FZ95-NZIQE9fw3A8Sgcz2BKep6MlC_Kng0dlf1ehabQ/edit#heading=h.kf3ndl27679m
22:59
<ljharb>
i think i'm going to step out of the convo myself; this is reminding me of the completely BS response i get to "brand checking" desires, and i'm a bit too tired to handle it well.
23:00
<jschoi>
Unfortunately I hear there is push back against considering tuples of unique symbols as WeakMap keys ...

Yeah, fortunately in this case, the object-replacement symbols would be the WeakMap’s values, not its keys. See https://github.com/js-choi/proposal-function-memo/blob/main/README.md#result-caches.

But we couldn’t use a WeakMap directly as the cache, though, if we used tuple keys, since tuples can’t contain objects anyway. But that’s fine. We’re using user-provided Map-likes anyway.

23:00
<shu>
because i don't value any of the motivations for either of those, personally, but that should mean i stay out of the conversation, not that i ridicule or block them
it is not the case i don't value the motivation, it is that i think i understand the motivation, and that it is the wrong thing to trade off, and that i find your equivocating of very different things in the language frustrating
23:01
<shu>
my reaction is not because "lol i don't care about robustness, get out of here with that proposal"
23:01
<jschoi>
We have certainly had conversations about records and tuples as recently as last December in the SES calls https://docs.google.com/document/d/1FZ95-NZIQE9fw3A8Sgcz2BKep6MlC_Kng0dlf1ehabQ/edit#heading=h.kf3ndl27679m
Sorry, I had meant whether there has been any recent activity around compositeKeys (https://github.com/tc39/proposal-richer-keys/blob/master/compositeKey/README.md), not around tuples.
23:02
<Kris Kowal>
in general, i want to minimize how much i trust other code.
Sympathetic to this position, since I find myself in situations where reentrance of guest code can violate my invariants. Protocols are actually a hazard in these situations.
23:03
<Kris Kowal>
But I do like collection protocols, though there is a design tension in performance.
23:03
<Kris Kowal>
Much depends on what kind of contract you’re providing.
23:04
<Kris Kowal>
In the situations where we find ourselves uncurryThis(String.prototype.set), the contract is not object oriented, not lazy bound, instances must be Sets and anything else is going to be ignored.
23:05
<Kris Kowal>
Whereas Array.prototype.slice.call(arguments) is from a very different design space.
23:05
<Kris Kowal>
And I like both of these subjectively.
23:07
<Kris Kowal>
The one thing that Map and Set do that I can’t do in user code is fast hashing on an opaque object, so I’d turn to them as a high performance building block for user code collections with rich protocols.
23:07
<Kris Kowal>
Thus, cake and eat it.