I agree the behavior aligns with the documentation and tests. On the other hand, there seem to be two scenarios where the lazily calculated size of infinity doesn't line up with the determinable product count.
One, referenced above, is when there's both an infinite size and a 0
so we know the product will be 0
but infinity is returned. Another is when an infinite size precedes a nil
so the product isn't actually lazily determinable but it returns infinity rather than nil
.
##
# Product count will be zero.
enums_zero = [[], 1..]
##
# Product count is unknown.
enums_nil = [1.., Enumerator.new {}]
Considering the current enum_product_total_size(VALUE enums)
roughly translated to Ruby:
def enum_product_total_size(enums)
enums.reduce(1) do |total, enum|
size = enum.size
return size if size.nil? || size.infinite?
return nil unless size.is_a?(Integer)
total * size
end
end
enum_product_total_size(enums_zero)
#=> Float::INFINITY
p enum_product_total_size(enums_nil)
#=> Float::INFINITY
It feels like we should short circuit and return nil
if any size is nil
and return 0
rather than Float::INFINITY
when both are present. One option would be to first check for any nil
then add a check for any 0
before proceeding with infinity and integer checks and multiplication.
def enum_product_total_size(enums)
sizes = enums.map(&:size)
return nil if sizes.include?(nil)
return 0 if sizes.include?(0)
return Float::INFINITY if sizes.include?(Float::INFINITY)
return nil unless sizes.all?(Integer)
sizes.reduce(1, :*)
end
enum_product_total_size(enums_zero)
#=> 0
p enum_product_total_size(enums_nil)
#=> nil
It seems to me the documented lazy sizes are wrong in these two cases. Would fixing these lazily calculated sizes to align with the actual product count be worth doing? It seems like a nice things to do to me, unless there are concerns that it's a breaking change since folk are relying on a dubious lazy Float::INFINITY
size that doesn't match the count.