Project

General

Profile

Actions

Feature #18498

closed

Introduce a public WeakKeysMap that compares by equality

Added by byroot (Jean Boussier) almost 3 years ago. Updated over 1 year ago.

Status:
Closed
Assignee:
-
Target version:
[ruby-core:107190]

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

Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #16038: Provide a public WeakMap that compares by equality rather than by identity ClosedActions
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0