Project

General

Profile

Actions

Feature #19069

closed

Default value assignment with `Hash.new` in block form

Added by sawa (Tsuyoshi Sawada) about 2 years ago. Updated almost 2 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:110401]

Description

This is a spin-out from #19063, and is a recapture of my comment https://bugs.ruby-lang.org/issues/19063#note-15.

I propose to change the behavior of Hash.new when it takes a block with its parameter signature absent. In such case, the evaluated value of the block should be assigned to the hash with the missing key in question (h3 below). When the block does take parameters, then the behavior should remain as is now (h1, h2 below).

h1 = Hash.new{|h, k| h[k] = "foo"}
h2 = Hash.new{|h, k| "foo"}
h3 = Hash.new{"foo"}

h1[:a] # => "foo"
h2[:a] # => "foo"
h3[:a] # => "foo"

h1 # => {:a=>"foo"}
h2 # => {}
h3 # => {:a=>"foo"}

This will solve a few problems. First, as discussed in #19063, many users make the mistake of writing Hash.new([]) when they actually mean Hash.new{|h, k| h[k] = []}, and I suspect this is partially due to the fact that the block {|h, k| h[k] = []} is too long, and the users are tempted to avoid writing so. With the proposed feature introduced, the users will be encouraged to naturally write the correct form Hash.new{[]}.

Second, some of the more advanced users than those mentioned above still make the mistake of writing Hash.new{foo} when they actually mean Hash.new{|h, k| h[k] = foo}, and the current proposal is to let them actually be equivalent.

Third, this will resemble a similar construct Array.new(5){[]} and they will make a good parallel.

Indeed, there are situations where the intended behavior is to just run a routine without assigning a new key-value pair to the hash. Examples of current code may be as follows:

h4 = Hash.new{some_routine_to_take_care_of_the_missing_key_situation}
h5 = Hash.new{raise WrongKeyBlahBlahError}

Such blocks would need to be prepended by a parameter signature |h, k| in order to avoid unwanted results. I do not think such transition would be a huge pain.


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #19063: Hash.new with non-value objects should be less confusingRejectedActions
Actions #1

Updated by sawa (Tsuyoshi Sawada) about 2 years ago

  • Description updated (diff)

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

Adding a new element in the default proc will add by just referencing.
I'm not for doing it implicitly, and ditto for #19063.

Updated by sawa (Tsuyoshi Sawada) about 2 years ago

@nobu (Nobuyoshi Nakada) (Nobuyoshi Nakada) I interpret that your concern is that the hash will grow endlessly by random referencing (unintentionally or by an attack). I have some possible alternative modifications to the proposal for that.

  1. Warn when a block without parameter signature Hash.new{"foo"} is used.

  2. Forget about looking at the block arity, and explicitly control the behavior as below:

    h1 = Hash.new{|h, k| "foo"}
    h2 = Hash.new{"foo"}
    h3 = Hash.new(assign: true){|h, k| "foo"}
    h4 = Hash.new(assign: true){"foo"}
    
    h1[:a] # => "foo"
    h2[:a] # => "foo"
    h3[:a] # => "foo"
    h4[:a] # => "foo"
    
    h1 # => {}
    h2 # => {}
    h3 # => {:a=>"foo"}
    h4 # => {:a=>"foo"}
    

    This will decrease the usefulness of the proposal a little bit for the use case mentioned in the proposal, but writing Hash.new(assign: true){"foo"} may still be a bit handier than writing Hash.new{|h, k| h[k] = "foo"}.

  3. Implement this feature under a separate method Hash.new!. Users will be aware that using this method can be potentially dangerous.

Actions #4

Updated by hsbt (Hiroshi SHIBATA) almost 2 years ago

  • Related to Feature #19061: Proposal: make a concept of "consuming enumerator" explicit added
Actions #5

Updated by hsbt (Hiroshi SHIBATA) almost 2 years ago

  • Related to deleted (Feature #19061: Proposal: make a concept of "consuming enumerator" explicit)
Actions #6

Updated by hsbt (Hiroshi SHIBATA) almost 2 years ago

  • Related to Feature #19063: Hash.new with non-value objects should be less confusing added

Updated by matz (Yukihiro Matsumoto) almost 2 years ago

  • Status changed from Open to Rejected

For compatibility's sake (and to avoid extra complexity), we reject this idea.

Matz.

Updated by Eregon (Benoit Daloze) almost 2 years ago

As a note this change could be useful to make the default_proc assignment thread-safe (avoid assigning twice for the same key).
It's not safe for Hash currently (I'll file a separate issue to discuss that) and neither for Concurrent::Hash (https://github.com/ruby-concurrency/concurrent-ruby/issues/970#issuecomment-1346338557).

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0