[#100689] [Ruby master Feature#17303] Make webrick to bundled gems or remove from stdlib — hsbt@...
Issue #17303 has been reported by hsbt (Hiroshi SHIBATA).
11 messages
2020/11/02
[#100852] [Ruby master Feature#17326] Add Kernel#must! to the standard library — zimmerman.jake@...
SXNzdWUgIzE3MzI2IGhhcyBiZWVuIHJlcG9ydGVkIGJ5IGpleiAoSmFrZSBaaW1tZXJtYW4pLg0K
24 messages
2020/11/14
[#100930] [Ruby master Feature#17333] Enumerable#many? — masafumi.o1988@...
Issue #17333 has been reported by okuramasafumi (Masafumi OKURA).
10 messages
2020/11/18
[#101071] [Ruby master Feature#17342] Hash#fetch_set — hunter_spawn@...
Issue #17342 has been reported by MaxLap (Maxime Lapointe).
26 messages
2020/11/25
[ruby-core:101094] [Ruby master Feature#17342] Hash#fetch_set
From:
eregontp@...
Date:
2020-11-26 14:37:03 UTC
List:
ruby-core #101094
Issue #17342 has been updated by Eregon (Benoit Daloze).
Another name for this is `compute_if_absent`.
It's notably the name used in concurrent-ruby:
http://ruby-concurrency.github.io/concurrent-ruby/1.1.5/Concurrent/Map.html#compute_if_absent-instance_method
And in Java, where it's even defined on Map (not just ConcurrentMap):
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function-
Having it as a built-in method, it also makes it possible to avoid computing the key's `#hash` twice, and potentially avoid redoing the lookup in the Hash.
Another way to do this pattern is to use the default block:
```ruby
RequestStore.store = Hash.new do |h, k|
h[k] = !MonitorValue.where('date >= ?', Time.now - 5.minutes).exists?
end
RequestStore.store[:monitor_value_is_delayed?]
```
Which already works fine.
And it has the advantage that if multiple places want to read from the Hash they don't have to repeat the code.
Is there a case this pattern wouldn't work and where `Hash#fetch_set` would work?
This pattern can be made to work with parallelism too, see [Idiomatic Concurrent Hash Operations](https://eregon.me/blog/assets/research/thesis-thread-safe-data-representations-in-dynamic-languages.pdf), page 83.
Regarding concurrency and parallelism, we need to define the semantics if we add this method.
Of course, the assignment should not be performed if there is already a key, it must be "put if absent" semantics
(`cache.fetch(key) { cache[key] = calculation }` is actually breaking that).
The question is whether the given block can be executed multiple times for a given key.
If not, it requires synchronization while calling the block, which can lead to deadlocks.
If yes, it doesn't require synchronization while calling the block which seems safer, but it means the block can be called multiple times.
----------------------------------------
Feature #17342: Hash#fetch_set
https://bugs.ruby-lang.org/issues/17342#change-88771
* Author: MaxLap (Maxime Lapointe)
* Status: Open
* Priority: Normal
----------------------------------------
I would like to propose adding the `fetch_set` method to `Hash`. It behaves just like `fetch`, but when using the default value (2nd argument or the block), it also sets the value in the Hash for the given key.
We often use the pattern `cache[key] ||= calculation`. This pattern however has a problem when the calculation could return false or nil, as in those case, the calculation is repeated each time.
I believe the best practice in that case is:
```ruby
cache.fetch(key) { cache[key] = calculation }
```
With my suggestion, it would be:
```ruby
cache.fetch_set(key) { calculation }
```
In these examples, each part is very short, so the `fetch` case is still clean. But as each part gets longer, the need to repeat cache[key] becomes more friction.
Here is a more realistic example:
```ruby
# Also using the key argument to the block to avoid repeating the
# long symbol, adding some indirection
RequestStore.store.fetch(:monitor_value_is_delayed?) do |key|
RequestStore.store[key] = !MonitorValue.where('date >= ?', Time.now - 5.minutes).exists?
end
RequestStore.store.fetch_set(:monitor_value_is_delayed?) do
!MonitorValue.where('date >= ?', Time.now - 5.minutes).exists?
end
```
There is a precedent for such a method: Python has it, but with a quite confusing name: `setdefault(key, default_value)`. This does not set a default for the whole dictionary as the name would make you think, it really just does what is proposed here. https://docs.python.org/3/library/stdtypes.html#dict.setdefault
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>