Feature #18498
closedIntroduce a public WeakKeysMap that compares by equality
Description
This is a clean take on #16038
Spec¶
Weak keys only¶
After a chat with @Eregon (Benoit Daloze) we believe that what would make the most sense and would be most useful would be
a "WeakKeysMap". Meaning the keys would be weak references, but the values would be strong references.
This behavior is also consistent with the terminology in other popular languages such as Javascript and Java were WeakMap
only have weak keys.
By default it would use equality semantic, just like a regular Hash
. Having an option to compare by indentity instead
could be useful, but not strictly required.
Immediate objects support¶
Many WeakMap implementation in other languages don't accept "immediate" objects (or their equivalent) in the weak slots.
This is because since they are never collected, the weak reference will never possibly expire.
ObjectSpace::WeakMap
currently accept them, but since both keys and values are weak references, there is legitimate use.
However in a WeakKeysMap
, using an immediate object as key should likely raise a TypeError
.
What is less clear is wether BIGNUM
(allocated Integer
) and dynamic symbols (allocated Symbol
) should be accepted.
I believe they shouldn't for consistency, so:
- No immediate objects
- No
Integer
- No
Symbol
member
method to lookup an existing key
For some use case, notably deduplication sets, a member
method that maps st_get_key
would be useful.
def member(key) -> existing key or nil
Not sure if member
is the best possible name, but #key
is already used for looking up a key by value.
That same method could be useful on Hash
and Set
as well, but that would be a distinct feature request.
Naming¶
Possible names:
::WeakMap
::WeakKeysMap
::WeakRef::Map
::WeakRef::WeakMap
::WeakRef::WeakKeysMap
::WeakRef::WeakHash
::WeakRef::WeakKeysHash
My personal, ligthly held, preference goes toward ::WeakRef::WeakKeysMap
.
Use cases¶
WeakSet¶
A WeakKeysMap
would be a good enough primitive to implement a WeakSet
in pure Ruby code, just like Set
is
implemented with a Hash
.
WeakSet
are useful for use cases such as avoiding cycles in an object graph without holding strong references.
Deduplication sets¶
Assuming WeakMap
have the #member
method.
A small variation on the "deduplicating constructors" use case, better suited for smaller but numerous objects.
The difference here is that we first build the object and then lookup for a pre-existing one. This is the
strategy used to deduplicate Active Record schema metadata.
class DeduplicationSet
def initialize
@set = WeakKeysMap.new
end
def dedup(object)
if existing_object = @set.member(object)
existing_object
else
@set[object] = true
object
end
end
end
Third party object extension¶
When you need to record some associated data on third party objects for which you don't control the lifetime.
A WeakMap can be used:
METADATA = WeakKeysMap.new
def do_something(third_party_object)
metadata = (METADATA[third_party_object] ||= Metadata.new)
metadata.foo = "bar"
end