From: intrip@...
Date: 2020-11-26T07:57:21+00:00
Subject: [ruby-core:101084] [Ruby master Feature#17342] Hash#fetch_set

Issue #17342 has been updated by jbeschi (jacopo beschi).


`fetch_set` mixes the concept of query with the concept of command and I think it's not a good approach.

Moreover I don't really see any big advantage in writing 

```ruby
cache.fetch(key) { cache[key] = calculation }
```

vs 

```ruby
cache.fetch_set(key) { calculation }
```

Therefore I don't understand why we should add it to the language.

----------------------------------------
Feature #17342: Hash#fetch_set
https://bugs.ruby-lang.org/issues/17342#change-88761

* 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>