From: "zverok (Victor Shepelev)" Date: 2022-01-30T09:58:32+00:00 Subject: [ruby-core:107354] [Ruby master Feature#18368] Range#step semantics for non-Numeric ranges Issue #18368 has been updated by zverok (Victor Shepelev). > Here's one idea: how about adding an #increment(n) method. For Numeric, Float, Time, Date, it would be an alias to +. For String it would be equivalent to doing succ n times. So Range#step would have the semantics of using begin.increment(step) iteratively. What about other, non-core classes? Will we have implicitly defined `Object#increment` to handle them, and if so, how it is defined? Or each and every library should define their own `#increment`, and if it doesn't, the range iteration just fails? In general, I believe this approach would render the feature virtually unusable. On the other hand, "It just uses `+`" follows the principle of least surprise, and is easy to explain and document. It is also totally easy to grasp why in cases where `+` is defined as concatenation, the result is the way it is (the same way that result of `('A'..'aa').to_a` might be "somewhat surprising" at first, but is easy to explain in hindsight). In general, what I am trying to do here is to make the semantics more intuitive, not more complicated to tailor some special case. PS: I might even suppose a reasonable use of the new proposed `step` with strings. For example, in Markdown markup language, the level of the header is designated by the number of `#` before it. So, one might do something like ```ruby PREFIXES = (''..).step('#').take(7) # => ["", "#", "##", "###", "####", "#####", "######"] # ...or even just PREFIXES = (''..'######').step('#').to_a # => ["", "#", "##", "###", "####", "#####", "######"] # and then use it like def render_header(h) "#{PREFIXES[h.level]} #{h.text}" end ``` Would this be an unreadable crime against common sense? I'd say not. YMMV. (I am not saying you can't do `'#' * h.level` or that it is worse. I am just saying that nothing "weird" seems to be in the semantics of `Range#step` even in the String case.) ---------------------------------------- Feature #18368: Range#step semantics for non-Numeric ranges https://bugs.ruby-lang.org/issues/18368#change-96253 * Author: zverok (Victor Shepelev) * Status: Open * Priority: Normal ---------------------------------------- I am sorry if the question had already been discussed, can't find the relevant topic. "Intuitively", this looks (for me) like a meaningful statement: ```ruby (Time.parse('2021-12-01')..Time.parse('2021-12-24')).step(1.day).to_a # ^^^^^ or just 24*60*60 ``` Unfortunately, it doesn't work with "TypeError (can't iterate from Time)". Initially it looked like a bug for me, but after digging a bit into code/docs, I understood that `Range#step` has an odd semantics of "advance the begin N times with `#succ`, and yield the result", with N being always integer: ```ruby ('a'..'z').step(3).first(5) # => ["a", "d", "g", "j", "m"] ``` The fact that semantic is "odd" is confirmed by the fact that for Float it is redefined to do what I "intuitively" expected: ```ruby (1.0..7.0).step(0.3).first(5) # => [1.0, 1.3, 1.6, 1.9, 2.2] ``` (Like with [`Range#===` some time ago](https://bugs.ruby-lang.org/issues/14575), I believe that to be a strong proof of the wrong generic semantics, if for numbers the semantics needed to be redefined completely.) Another thing to note is that "skip N elements" seem to be rather "generically Enumerable-related" yet it isn't defined on `Enumerable` (because nobody needs this semantics, typically!) Hence, two questions: * Can we redefine generic `Range#step` to new semantics (of using `begin + step` iteratively)? It is hard to imagine the amount of actual usage of the old behavior (with String?.. to what end?) in the wild * If the answer is "no", can we define a new method with new semantics, like, IDK, `Range#over(span)`? -- https://bugs.ruby-lang.org/ Unsubscribe: