4

Theory

Basically, I have a use-case where I would like to use primitives to store weak references to non-primitive values. If the value is no longer referenced anywhere else, then the entry should be GC'd from the WeakMap and checking the primitive key for an existing value would return false.

Is this possible to implement, and how so in a theoretical sense? If not, why isn't it possible to?

Use-case

Since people seem to be confused about the use-case, it's pretty straight-forward. Essentially, consider a function, when given a primitive, generates a non-primitive deterministically using an expensive operation. I expect a fair amount of these generated non-primitives to be used at run-time, and a large percent of them to be re-used.

For the non-primitives that have a reference held onto them, they are used as keys in an operation that coerces them back into the primitive that was used to generate them (this step cannot be avoided, unfortunately), that then must be used to re-generate the non-primitive again.

It would be convenient to have a WeakMap to access the original non-primitive, if it has already been made, instead of using the expensive operation to generate a copy of it, but currently I don't see a way to achieve this.

3 Answers3

4

Okay. My confusion with this question is that I assumed that what you wanted is a weak value map, which it appears is what you want, and this can be implemented in a straightforward manner (at least as straightforward as a weak key map). I'm using (slightly modified) terminology from Designing Efficient and Safe Weak References in Eiffel with Parametric Types. The JavaScript WeakMap is indeed a weak key map, and, as you seem to understand and D.W. states, it makes no sense to have weak references to primitive values, and thus primitive values as keys in a weak key map.

As far as a weak value map, it's implemented basically the same as a weak key map. For example, for a mark-sweep GC, during the sweeping phase if the GC notices that a value in a weak value map is dead it removes all entries in the map referencing it. A weak key map is basically the same only it considers the keys. The Eiffel paper also talks about weak vectors and doubly weak maps where either the key or the value dying results in the entry being removed.

A weak reference primitive as described in the paper or as in e.g. Java's WeakReference would make this a straightforward exercise. You'd just wrap a Dictionary<Key, WeakReference<Value>> to clean out entries on access, say.

Derek Elkins left SE
  • 12,179
  • 1
  • 30
  • 43
1

It would be pointless to have a primitive as the key in a Javascript WeakMap, as you can't/don't garbage-collect primitives, and a WeakMap is entirely about whether or not GC will garbage-collect the keys in the WeakMap. No purpose would be served by allowing the key to be a primitive.

See also https://softwareengineering.stackexchange.com/q/324345/34181.

D.W.
  • 167,959
  • 22
  • 232
  • 500
0

Probably a bit late for that thread, but this is how I implemented the requirements using WeakRef and FinalizationRegistry...

/**
 * A map that holds weak references to its values, allowing them to be garbage collected if there are no other strong references to them.
 */
export class WeakValueMap<K, V extends WeakKey> {
    private map = new Map<K, WeakRef<V>>();
    private registry = new FinalizationRegistry<K>((key) => this.map.delete(key));
set(key: K, value: V) {
    // Unregister the previous weak reference if it exists
    const currentRef = this.map.get(key);
    if (currentRef !== undefined) {
        this.registry.unregister(currentRef);
    }

    // Create a new weak reference and register it with the finalization registry
    const newRef = new WeakRef(value);
    this.map.set(key, newRef);
    this.registry.register(value, key, newRef);
}

get(key: K) {
    return this.map.get(key)?.deref();
}

} ```