Issue #21780 has been updated by zverok (Victor Shepelev). @mame My thinking (already outlined above) goes this way: * while `Enumerator#size`, I believe, is not used extensively, it might in the future (this very change of `Enumerator.produce` call-sequence brings some attention to it) * `Enumerator::Lazy#take` respecting the infinity size is a (weak) argument that `Enumerator#size` matters at least sometimes, and it is good to have it aligned with the user's intuitions * `Enumerator.produce` being an _infinite_ enumerator **corresponds to its design**. Yes, it can be broken from by an _exception_, but its more general behavior is "loop infinitely". So, basically: * if the developer relies on the default behavior (which produces an infinite sequence), they have the default size (infinity) * if they consciously adjust behavior, by throwing exceptions, it might be OK for them (if they care at all), to adjust size to `nil`/"unknown beforehand" or some known value. The _only_ reason the default for `size:` was chosen to be `nil` is that `Enumerator.produce {...}.to_set` was "broken". And, honestly, I don't think it is a good reason to muddy the semantics. With @knu's proposal to just stop checking `#size` is `Enumerator#to_set` seems to resolve this. Are there clear advantages to keep it `nil` now, if the enumerator is _infinite by design_? TL;DR: I don't think many devs care, but for those who do, `Infinity` is more reasonable for this enumerator. ---------------------------------------- Bug #21780: Change the default size of Enumerator.produce back to infinity https://bugs.ruby-lang.org/issues/21780#change-115742 * 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/