acutmore / proposal-keyby

1 stars 1 forks source link

Why not expose the underlying values? #6

Open bakkot opened 9 months ago

bakkot commented 9 months ago

Maybe I'm missing where this is documented, but why not expose the constituents? When I've done something like the ===-preserving CompositeKey in userland, I used a frozen array.

This has some advantages - it lets you do stuff like sortKeys((a, b) => a === b ? 0 : elementWiseCompare(a, b)), where elementWiseCompare is the normal lexicographic comparison on arrays. You can't implement that without access to the constituents.

acutmore commented 9 months ago

The primary reason is privacy by default. If a class used a composite key to expose a way of storing itself in a keyed collection, it wouldn't leak potential implementation details of how it stores its internal fields.

This could be achieved by consciously constructing a key that doesn't do this. Though conversely public frozen array style Keys can be built on top of private Keys.

Sorting is interesting, I did kinda like the ideas from R&T about throwing in valueof for Tuple so a follow on could let it used as a way of doing multi level compare. (a, b) => #[a.f1, a.f2] > #[b.f1, b.f2].

The second reason is that there is no concerns about -0 being normalized to or not equal to 0 if the value is hidden.

A third reason was potential that object constituents are not necessarily kept alive by the key, only their "identity" is required to be preserved.

bakkot commented 9 months ago

The primary reason is privacy by default.

If so, I'm inclined to think that the usefulness of exposing values outweighs the costs of losing that privacy by default, especially given how easy it is to mask it if you want (i.e., make an internal map of CompositeKeys to empty objects, and expose the objects instead).

The second reason is that there is no concerns about -0 being normalized to or not equal to 0 if the value is hidden.

Well, no, you still have to answer if CompositeKey(0).equals(CompositeKey(-0)). So you have to handle normalization either way.

A third reason was potential that object constituents are not necessarily kept alive by the key, only their "identity" is required to be preserved.

While that's true, I have a hard time imagining cases where that would be relevant.

acutmore commented 9 months ago

Well, no, you still have to answer if CompositeKey(0).equals(CompositeKey(-0)). So you have to handle normalization either way.

I should have said that this aspect is to do with interning. If CompositeKey(0) === CompositeKey(0) and CompositeKey(0) === CompositeKey(-0) then this implies that the -0 and 0 are normalised to the same value which was a point of contention for R&T (even if this is what Map and Set do).