Project

General

Profile

Bug #19530

Updated by dstosik (David Stosik) almost 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.

Back