Issue #21780 has been updated by knu (Akinori MUSHA). zverok (Victor Shepelev) wrote in #note-7:
2) Somebody uses `Enumerator.produce` alongside other types of enumerators. In some branch of their code, they do `raise "Can't do this operation" if enum.size == Float::INFINITY`. The compatibility is broken for them.
I consider that kind of usage as a safeguard that usually works in development rather than something that should be relied upon in production code. However, the same goes for the change in Enumerator#to_set, so we might want to consider reverting it in the first place.
3) Somebody relies on `Enumerator.produce { ... }.take(5)` to have non-`nil` size, throwing it around as a duck-typed array. The compatibility is broken for them.
`Enumerator.produce { ... }.take(5)` returns an array. Did you mean `Enumerator.produce { ... }.lazy.take(5)`? If so, then yes, the compatibility is broken for them.
4) (Just to expand the scope of _possible_ compatibility studies) Somebody might've suddenly had code like this, and it is now also broken: ```ruby Enumerator.produce(size: 5) { it.merge(size: it[:size] + 1) }.take(8) #=> [{size: 5}, {size: 6}, {size: 7}, {size: 8}, {size: 9}, {size: 10}, {size: 11}, {size: 12}] ```
This should never have been allowed. It was my mistake I did not prohibit it.
Intuitively, I would say that (2) is the most basic case that shouldn't be broken; (3) is a (weak) evidence to the same; while both (1) and (4) are both a "collateral damage" that should be accepted (because if we treat compatibility with any real rigor, no changes should be made at all, "any change breaks _somebody's_ usecase").
I think (3) is more important than (2), and I'm not absolutely sure about how (1) and (3) compare in importance. If the concern about (1) could be eliminated by reverting Enumerator#to_set, we would be able to save both.
Is there any study that I am not aware of that says that (1) is the widespread case and breaking it will outrage a huge part of the community, while breaking 2-3 (_as well as_ the general semantics of the method) is negligible?
Or maybe there is some evidence/discussion that it is authors of elaborate enumerators who wouldn't understand a very small (and well-explained semantically) change to fix the incompatibility, while those who would expect this enumerator to be infinite should just swallow it and add `size: Float::INFINITY` maybe in a dozen places in their code?
I don't have any data about how widespread each of these use cases is, or even how Enumerator.produce is used in production code. I just know Enumerator.produce is still immature and would love to hear from more users about how they use it. ---------------------------------------- Bug #21780: Change the default size of Enumerator.produce back to infinity https://bugs.ruby-lang.org/issues/21780#change-115731 * Author: zverok (Victor Shepelev) * Status: Open * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- In #21701 a new argument `size:` was introduced, and its default value is `nil` (unknown). While I support the new argument, I'd argue that the default should be `Float::INFINITY`. **Reasoning:** By _design_, `Enumerator.produce` is infinite (there is no internal condition to stop iteration), and the simplest, most straightforward usages of the method would produce _definitely infinite_ iterators, which the user than can limit with `take`, or `take_while` or similar methods. To produce the enumerator that will stop by itself requires explicit raising of `StopIteration`, which I expect to be a (slightly) advanced technique, and those who use it might be more inclined to provide additional arguments to clarify the semantics. While `Enumerator#size` is hardly frequently used now (other than in `#to_set`, which started the discussion), it might be in the future, and I believe it is better to stick with more user-friendly defaults. Now: ```ruby # very trivial enumerator, but if you want it to have "proper" size, you need # to not forget to use an elaborate argument and type additional 21 characters Enumerator.produce(1, size: Float::INFINITY, &:succ) # already non-trivial enumerator, which is hardly frequently used, but the # current defaults correspond to its semantics: Enumerator.produce(Date.today) { raise StopIteration if it.tuesday? && it.day.odd? it + 1 } ``` With my proposal: ```ruby # trivial, most widespread case: Enumerator.produce(1, &:succ).size #=> Infinity # non-trivial case, with the enumerator designer clarifying their # intention that "we are sure it stops somewhere": Enumerator.produce(Date.today, size: nil) { raise StopIteration if it.tuesday? && it.day.odd? it + 1 } ``` -- https://bugs.ruby-lang.org/