tc39 / proposal-symbols-as-weakmap-keys

Permit Symbols as keys in WeakMaps, entries in WeakSets and WeakRefs, and registered in FinalizationRegistries
http://tc39.es/proposal-symbols-as-weakmap-keys
MIT License
90 stars 7 forks source link

Helpful predicate functions #24

Closed acutmore closed 2 years ago

acutmore commented 2 years ago

I'm spinning an issue off from @rricard 's comment here: https://github.com/tc39/proposal-symbols-as-weakmap-keys/pull/23#pullrequestreview-897984341

I remember starting to discuss the different variants of helper functions that could land with this proposal, but I don't recall if we came to a clear answer or not as I failed to take detailed notes.

cc: @ljharb @syg @codehag


Some options for helpers:

A) WeakMap.isValidKey et al

There would also be WeakSet.isValidValid, WeakRef.isValidTarget and FinalizationRegistry.isValidTarget, which would likely all share the same function instance if the logic is the same. This predicate returning true/false would be a direct indication of if the same value would-not/would throw if attempted to be used with an instance of the class.

One issue that has been raised with this helper is that it is likely to 'lock' in the set of valid values; if the only time the logic for this function can change is to encompass new types when they are added to the language, and couldn't change (across versions) for existing types. For example: relaxing the rule to allow 'well-known' symbols in the future. This could impact Record&Tuple, if the initial MVP spec for them says they are not valid WeakMap keys, then this could mean they can never be, as the predicate would need to change its result.

B) Object.isUnforgeable

(Doesn't necessarily need to be on Object). The key idea here, as I understand it, is to de-couple the predicate from if the value can be used in a weak collection. i.e. it can be used as an indicator but does not necessarily perfectly divide values into the two sets of valid/invalid WeakMap keys.

This could potentially allow the set of valid WeakMap keys to change as this predicate itself would always give the same result for a given input across versions. For example isUnforgeable(Symbol.iterator) may always return false**, but registered symbols may throw when used as WeakMap keys in one version but then relaxed in future versions. Or for Record&Tuple, isUnforgeable(Tuple(Symbol)) could always return true, but maybe this does not guarantee that it is also a valid WeakMap key, relaxing this could then be a future separate proposal.

** `isUnforgeable(Symbol.iterator) === false` may be wrong, it would depend on how we interpret forgeability. ```js // index.js delete Array.prototype[Symbol.unscopables]; delete Symbol.prototype.constructor; delete globalThis.Symbol; // is `@@unscopables` now unreachable? maybe not as [HasBinding](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-object-environment-records-hasbinding-n) still references it const unscopables = new ShadowRealm().evaluate(`Symbol.unscopables`); // have I re-forged it? ```

?) Other options?

Z) No new predicate functions

One option is to not introduce new predicates in the spec itself. This would leave userland to create their own predicates and/or use try/catch.

ljharb commented 2 years ago

Userland already does create their own predicates using try/catch; so I remain confused why there'd be a web compatibility issue with changing the predicate logic, but not with changing the throw logic.

acutmore commented 2 years ago

Predicate functions were talked about in the last Record and Tuple monthly.

To keep the proposal focused the predicates can be pursued in a separate “symbol-predicates” proposals.

Until then @ljharb has kindly created npm packages:

https://npmjs.com/is-registered-symbol https://npmjs.com/is-well-known-symbol https://npmjs.com/intl-fallback-symbol