Bug #19530
Updated by dstosik (David Stosik) over 2 years ago
Hi everyone. 👋🏻
We recently discovered that `Array#sum` and `Enumerable#sum` will output different results in some edge cases.
Here's the smallest script I managed to write to reproduce the issue:
``` ruby
class Money
def initialize(amount)
@amount = amount.to_f
end
def +(other)
self.class.new(@amount + other.to_f)
end
def to_f
@amount
end
end
p [7.0].each.sum(Money.new(0)) [7.0].each.sum(Money.new(0)).class #=> #<Money:0x00000001005b35f0 @amount=7.0> Money
p [7.0] .sum(Money.new(0)) .sum(Money.new(0)).class #=> 7.0 Float 💥
```
I understand that it is expected that `#sum` may not honor custom definitions of the `#+` method (particularly when the summed values are `Float`).
However, I would like to bring your attention to the fact that, in the example above, calling `#sum` on an `Array` of `Float` values and calling `#sum` on an `Enumerable` that yields the same `Float` values will return results of different types.
I've reproduced the same behaviour with multiple versions of Ruby going from 2.6.5 to 3.2.1.
Ideally, I would expect `[7.0].sum(Money.new(0))` to return a `Money` object identical to the one returned by `[7.0].each.sum(Money.new(0))`.
I think it would make sense if at least they returned an identical value (even if it is a `Float`).
Addendum: I forgot to mention [this extract](https://github.com/ruby/ruby/blob/da9c84f859db292ab1127f7ca9b7741fff06557b/array.c#L8133-L8137) of the `Array#sum` documentation:
> When no block is given, returns the object equivalent to:
>
> ``` ruby
> sum = init
> array.each {|element| sum += element }
> sum
> ```
With the example above, it would indeed return a `Money` object.