Project

General

Profile

Actions

Feature #20818

open

Allow passing a block to Hash#store (to update current value)

Added by furunkel (Julian Aron Prenner) about 2 months ago. Updated 10 days ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:119632]

Description

I would like to propose a block form for Hash#store. In addition to passing a value, it should also be allowed to pass a block.
If passed a block instead of a value, the block is called with the current value or, if unset, the hash's default value; the block's return value will be the value that is stored.
This is similar to e.g., computeIfPresent/computeIfAbsent in Java (I think).

I can think of several situations where this would be useful, in particular for caches and counters.
For instance:

counts = {}
elements.each do |element|
  counts.store(element){ (_1 || 0) + element.weight}
end

or even more elegant with a default value:

counts = {}
counts.default = 0
elements.each do |element|
  counts.store(element){ _1 + element.weight}
end

Moreover, using the block form we should be able to do operations such as h[k] ||= x, h[k] -= x, h[k] += x, or more generally h[k] = f(h[k]), with a single "hashing round-trip".
If I'm not mistaken, currently these involve two separate calls to #[] and #[]= (with two calls to #hash?).

Finally, this makes #store a proper dual of #fetch which, similarly, can be passed a block.

I have an experimental implementation of this (GitHub PR) at: https://github.com/ruby/ruby/pull/11956

Updated by mame (Yusuke Endoh) about 2 months ago

Discussed at the dev meeting. @matz (Yukihiro Matsumoto) said "I don't like the style so much, but if the patch is safe and actually improves performance, I'm willing to consider it. I'd like you to show the benchmark."

I am not sure if it is safe to execute arbitrary Ruby code in the RHASH_UPDATE_ITER callback. For example, what will happen if we update the hash itself inside the block of Hash#store, like h.store(:key){ h[:another_key] = 1 }? Does the patch prohibit this? If it is allowed, we need to care the possibility that rehash may occur.

Updated by AMomchilov (Alexander Momchilov) 10 days ago

This is related to my Hash#exchange_value proposal, so I'll link that here. https://bugs.ruby-lang.org/issues/20300

I guess the difference is whether you already know the replacement you want to use instead, or if you'd like to compute it lazily when the key is already set.

Actions

Also available in: Atom PDF

Like0
Like0Like0