Issue #19324 has been updated by sawa (Tsuyoshi Sawada).
zverok (Victor Shepelev) wrote in #note-4:
[I]s there a design plan to change _many_ of the
methods (`Array.product` in addition to `Enumerable.product`, `Array.zip`, `Hash.merge`, I
dunno, maybe even `Array.concat`?..) to the `Module.method` protocol? Or, would
`Enumerable.product` be the **only** sore exclusion from what is consistently done
throughout Ruby?
I believe quite many people are in favor of `Array.product` and/or `Array.zip`. I wish the
implementation/existence of `Enumerator.product` will work as a pressure towards
implementing these methods as well. However, I do not think many other methods like those
you mention would need to be handled similarly for the sake of consistency; (i)
`Array#product` and `Array#zip` are different from (ii) `Hash#merge` and `Array#concat`.
The (ii) methods with multiple arguments are nothing more than a repetition of binary
operations (and furthermore, they are associative):
```ruby
[1, 2, 3].concat([4, 5, 6], [7, 8, 9]) ==
[1, 2, 3].concat([4, 5, 6]).concat([7, 8, 9]) ==
[1, 2, 3].concat([4, 5, 6].concat([7, 8, 9]))
# => [1, 2, 3, 4, 5, 6, 7, 8, 9]
{a: 1}.merge({b: 2}, {c: 3}) ==
{a: 1}.merge({b: 2}).merge({c: 3}) ==
{a: 1}.merge({b: 2}.merge({c: 3}))
# => {:a=>1, :b=>2, :c=>3}
```
whereas the (i) methods are not:
```ruby
[1, 2].zip([3, 4], [5, 6]) # => [[1, 3, 5], [2, 4, 6]]
[1, 2].zip([3, 4]).zip([5, 6]) # => [[[1, 3], 5], [[2, 4], 6]]
[1, 2].zip([3, 4].zip([5, 6])) # => [[1, [3, 5]], [2, [4, 6]]]
[1, 2, 3].zip([4, 5, 6], [7, 8, 9]) # => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
[1, 2, 3].zip([4, 5, 6]).zip([7, 8, 9]) # => [[[1, 4], 7], [[2, 5], 8], [[3, 6], 9]]
[1, 2, 3].zip([4, 5, 6].zip([7, 8, 9])) # => [[1, [4, 7]], [2, [5, 8]], [3, [6, 9]]]
```
Thus, the (i) methods with multiple arguments have a firm position. They have stronger
motivation to exist as an independent concept, and accordingly, stronger motivation for
their receiver and the arguments to be treated symmetrically.
----------------------------------------
Feature #19324: Enumerator.product => Enumerable#product
https://bugs.ruby-lang.org/issues/19324#change-101194
* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
I know it might be too late after introducing a feature and releasing a version, but I
find `Enumerator.product` quite confusing, and can't find any justification in
#18685.
**Problem 1: It is `Array#product` but `Enumerator.product`**
```ruby
[1, 2].product([4, 5])
# => [[1, 4], [1, 5], [2, 4], [2, 5]]
# Usually, when we add methods to Enumerable/Enumerator which
# already array had before, it is symmetric, say...
[1, nil, 2, 3].compact #=> [1, 2, 3]
[1, nil, 2, 3].lazy.compact.first(2) #=> [1, 2]
# But not in this case:
[1, 2].lazy.product([4, 5]).first(2)
# undefined method `product' for #<Enumerator::Lazy: [1, 2]> (NoMethodError)
# Because you "just" need to change it to:
Enumerator.product([1, 2].lazy, [4, 5]).first(2)
# => [[1, 4], [1, 5]]
```
No other method was "promoted" from Array this way
And in general, I believe core methods tend to belong to the first object in the
expression and not be free module methods, Elixir style.
**Problem 2: It is one letter different from `Enumerator.produce`**
I understand I might be biased here (as a person who proposed `produce`), and that method
is not as popular (yet?) as I hoped, but still, two methods that do completely different
things and differ by one letter, both being somewhat vague verbs (so it is easy to confuse
them unless you did a lot of math and "product" is firmly set for set product in
your head).
I believe that EITHER of two problems would be concerning enough, but the combination of
them seems to be a strong enough argument to make the change?.. (Maybe with graceful
deprecation of module method in one next version, but, considering the Ruby 3.2 is just
released, maybe vice versa, fix the problem in the next minor release?..)
--
https://bugs.ruby-lang.org/