From: Austin Ziegler Date: 2020-12-03T17:57:38-05:00 Subject: [ruby-core:101232] Re: [Ruby master Feature#17342] Hash#fetch_set --===============2060904254== Content-Type: multipart/alternative; boundary="0000000000004cc60205b5974abb" --0000000000004cc60205b5974abb Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I=E2=80=99m mostly doing Elixir these days, and one of the caching modules = I use has as its main interface `fetch_or_set`, so I think that=E2=80=99s the bes= t choice if this is present. On the other hand, Nobu=E2=80=99s refinement implementa= tion looks pretty good (and I say this not using refinements yet, even though they=E2=80=99ve been around for a while). -a ``` On Thu, Dec 3, 2020 at 2:13 PM wrote: > Issue #17342 has been updated by Eregon (Benoit Daloze). > > > shyouhei (Shyouhei Urabe) wrote in #note-13: > > Yes I=E2=80=99m quite sure he is dead serious. There are idiots who w= rite > nonsense codes. As a language ruby has to be fool-proof. > > I think such semantics (calling #hash only once) could be part of the > method documentation. > OTOH calling `#hash` twice is probably not very expensive, and for some > cases (e.g. Integer, String) can be optimized by a JIT or by memoizing th= e > hash on the instance. > > I don't like the name `fetch_set` much (does it fetch a Set?). > I think `fetch_or_set` or `cache` are better. > > ---------------------------------------- > Feature #17342: Hash#fetch_set > https://bugs.ruby-lang.org/issues/17342#change-88908 > > * 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] ||=3D 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] =3D 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] become= s > 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] =3D !MonitorValue.where('date >=3D ?', Time.now= - > 5.minutes).exists? > end > > RequestStore.store.fetch_set(:monitor_value_is_delayed?) do > !MonitorValue.where('date >=3D ?', 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: > > --=20 Austin Ziegler =E2=80=A2 halostatue@gmail.com =E2=80=A2 austin@halostatue.c= a http://www.halostatue.ca/ =E2=80=A2 http://twitter.com/halostatue --0000000000004cc60205b5974abb Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I=E2=80=99m mostly doing Elixir these day= s, and one of the caching modules I use has as its main interface `fetch_or= _set`, so I think that=E2=80=99s the best choice if this is present. On the= other hand, Nobu=E2=80=99s refinement implementation looks pretty good (an= d I say this not using refinements yet, even though they=E2=80=99ve been ar= ound for a while).

-a
<= div>```

On Thu, Dec 3, 2020 = at 2:13 PM <eregontp@gmail.com= > wrote:
Issue #17342 has been updated by Ereg= on (Benoit Daloze).


shyouhei (Shyouhei Urabe) wrote in #note-13:
> Yes I=E2=80=99m quite sure he is dead serious.=C2=A0 There=C2=A0 are i= diots who write nonsense codes.=C2=A0 As a language ruby has to be fool-pro= of.

I think such semantics (calling #hash only once) could be part of the metho= d documentation.
OTOH calling `#hash` twice is probably not very expensive, and for some cas= es (e.g. Integer, String) can be optimized by a JIT or by memoizing the has= h on the instance.

I don't like the name `fetch_set` much (does it fetch a Set?).
I think `fetch_or_set` or `cache` are better.

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

* 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 b= lock), it also sets the value in the Hash for the given key.

We often use the pattern `cache[key] ||=3D calculation`. This pattern howev= er has a problem when the calculation could return false or nil, as in thos= e case, the calculation is repeated each time.

I believe the best practice in that case is:

```ruby
cache.fetch(key) { cache[key] =3D 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 cl= ean. But as each part gets longer, the need to repeat cache[key] becomes mo= re 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|
=C2=A0 RequestStore.store[key] =3D !MonitorValue.where('date >=3D ?&= #39;, Time.now - 5.minutes).exists?
end

RequestStore.store.fetch_set(:monitor_value_is_delayed?) do
=C2=A0 !MonitorValue.where('date >=3D ?', Time.now - 5.minutes).= exists?
end
```

There is a precedent for such a method: Python has it, but with a quite con= fusing 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 d= oes what is proposed here. https://do= cs.python.org/3/library/stdtypes.html#dict.setdefault



--
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=3Dunsubscribe= >
<http://lists.ruby-lang.org/cgi-bin/m= ailman/options/ruby-core>


--
--0000000000004cc60205b5974abb-- --===============2060904254== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline Unsubscribe: --===============2060904254==--