[ruby-core:123319] [Ruby Feature#21615] Introduce `Array#values`
Issue #21615 has been reported by matheusrich (Matheus Richard). ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning `self`. Implementation could simply alias to `itself`: ```ruby class Array alias_method :values, :itself end ``` This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by nobu (Nobuyoshi Nakada). matheusrich (Matheus Richard) wrote:
Add `Array#values`, returning `self`. Implementation could simply alias to `itself`:
I think it should be `Array.new(*self)`. `Hash#values` returns a new `Array`, non-frozen and independent from the receiver. ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114684 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning `self`. Implementation could simply alias to `itself`: ```ruby class Array alias_method :values, :itself end ``` This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by nobu (Nobuyoshi Nakada). matheusrich (Matheus Richard) wrote:
Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because:
"PoLS" is a word not to say at a proposal. That word is negative to convince us. We are not sure who started to say the word , but Ruby itself hasn't advertised it.
With `Array#values`:
```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ```
Why not to introduce `Array#each_value`? ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114685 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning `self`. Implementation could simply alias to `itself`: ```ruby class Array alias_method :values, :itself end ``` This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by matheusrich (Matheus Richard).
I think it should be Array.new(*self).
I can do that, sure.
That word is negative to convince us.
Noted! Good to know.
Why not to introduce Array#each_value?
I think it feels clunkier than `array.values.each`, but is an alternative I considered. IMO it still makes me expect that a `#values` method would exist since it would be a word present in `values_at`, `fetch_values` and `each_value`. IMO *both* could be added, so Array has an interface even more similar to Hash, but I decided to keep this small. ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114686 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning `self`. Implementation could simply alias to `itself`: ```ruby class Array alias_method :values, :itself end ``` This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by Dan0042 (Daniel DeLorme). I understand the idea of making core collection classes ducktype-compatible, but in that case this proposal should also include `Set#values` ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114688 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning a copy of the elements of `self`. This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by matheusrich (Matheus Richard). @Dan0042 I considered that. I thought it would be too much for one PR. I'll wait for more feedback, and if people are positive about this, I'll propose `Set#values` too. ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114689 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning a copy of the elements of `self`. This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by brightraven (Billy G. Jaime Beltran). matheusrich (Matheus Richard) wrote in #note-6:
@Dan0042 I considered that. I thought it would be too much for one PR. I'll wait for more feedback, and if people are positive about this, I'll propose `Set#values` too.
One principle I have enjoyed heavily from other programming languages that use this type of duck typing is the idea of a higher abstraction called a sequence[1] over which anything can be iterated, be it a set, a hash or an array. It would be pleasant to have this for all collections equally. 1. https://clojure.org/reference/sequences ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114849 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning a copy of the elements of `self`. This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by mame (Yusuke Endoh). This feels like opening Pandora's box. If this is accepted, I can foresee the following discussions arising, just off the top of my head: * Should `Array#keys` be defined as a method that returns `(0...ary.size).to_a`? * `Array#find_index` and `Hash#key` are (roughly) counterparts. Should they be aliased to each other? * Should `Hash#each_key` and `Array#each_index` also be aliased to each other? * What about order-related operations? Wouldn't we also need methods like `Hash#reverse`, `Hash#rotate`, and `Hash#sort`? (Note that `Hash#sort` would be incompatible with `Enumerable#sort`). * Should methods like `Array#rehash` and `Array#compare_by_identity` be provided (perhaps as no-ops)? * Wouldn't `Array#default` and `Array#default=` also be necessary? * Should operators like `Array#<=` be defined to align with `Hash#<=`? * While `Array#transform_values` could be defined straightforwardly, how should `Array#transform_keys` behave? * The different meanings of `Array#include?` and `Hash#include?` are surprising. * The different meanings of `Array#assoc` and `Hash#assoc` are surprising. After all, I personally feel that `Array` and `Hash` are not inherently polymorphic. If you want to use them polymorphically, I think you should limit their polymorphic use to `#[]` only. ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114861 * Author: matheusrich (Matheus Richard) * Status: Open ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning a copy of the elements of `self`. This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
Issue #21615 has been updated by matz (Yukihiro Matsumoto). Status changed from Open to Rejected HI, * Don't use principle of least surprise as your rationale, since background and assumption vary person to person * I don't think Hashes can be naturally considered as Arrays with non-integer indexes. You can tell it from Hash#each behavior, for example * **Don't mention principle of least surprise in the discussion here** Matz. ---------------------------------------- Feature #21615: Introduce `Array#values` https://bugs.ruby-lang.org/issues/21615#change-114906 * Author: matheusrich (Matheus Richard) * Status: Rejected ---------------------------------------- ## Motivation In Ruby code, it's common to accept arrays and hashes and treat them uniformly as collections of values. `Hash` exposes `#values`, but `Array` does not, which pushes developers toward `is_a?`/`respond_to?` branching. Following the **Principle of Least Surprise**, users may reasonably expect `Array#values` to exist because: * Both `Array` **and** `Hash` already implement `#values_at`. * `Hash` implements `#values` but `Array` does not. ### Example Today: ```ruby def normalize_records(input) items = input.respond_to?(:values) ? input.values : input items.each do |item| do_stuff_with_item(item) end end ``` With `Array#values`: ```ruby def normalize_records(input) input.values.each do |item| do_stuff_with_item(item) end end ``` ## Proposal Add `Array#values`, returning a copy of the elements of `self`. This yields a uniform interface for `Array` and `Hash` values without type checks. ### Alternatives considered * `Enumerable#values`: defaulting to `to_a`, but I found it too broad of a change. * `Array#each_value`: redundant as `Array#each` already covers iteration. Patch: https://github.com/ruby/ruby/pull/14641 -- https://bugs.ruby-lang.org/
participants (6)
-
brightraven (Billy G. Jaime Beltran) -
Dan0042 (Daniel DeLorme) -
mame (Yusuke Endoh) -
matheusrich (Matheus Richard) -
matz (Yukihiro Matsumoto) -
nobu (Nobuyoshi Nakada)