crystal icon indicating copy to clipboard operation
crystal copied to clipboard

Export Steppable::StepIterator's ivars as getters

Open docelic opened this issue 1 year ago • 5 comments

Hello,

With Range, one can create it and then query its begin and end values:

r = 10..20
r.begin # => 10
r.end # => 20

However, if a step is used, then the object isn't a Range but Steppable::StepIterator. It has ivars @current, @limit, @step, and @excusive, but they aren't getters and can't be queried from outside to find out more about the object:

r = (10..20).step 2
r.current # => undefined method
r.limit # => undefined method

Could the four ivars be exported as getters?

docelic avatar Dec 18 '23 20:12 docelic

Stdlib's iterator implementations are private types and essentially implementation details. They're not supposed to be seen as individual types, instead they ought to be just a generic Iterator(T) interface. For that reason Steppable::StepIterator etc. are private types and undocumented.

Adding public API methods doesn't really make sense in that context.

However, I'm personally a bit sceptical about the concept of hiding types that are exposed anyway when API methods return their instances. Users will inevitably be in contact with those types in error traces, typof and .class reflection and various debugging mechanisms. And IMO it should be a good idea to have some documentation on them. Otherwise you're essentially forced to dig in the stdlib sources to even find out what it is.

So I'd be interested in your use cases. What do you need the ivar values for?

straight-shoota avatar Dec 18 '23 20:12 straight-shoota

Well, talking about a specific use case:

  1. In https://github.com/crystallabs/virtualtime I do a lot of date/time matching. Negative integers such as -1 get converted to the last day of year/month/week/whatever. However, a person can specify not only plain integers, but also their arrays, ranges, and/or ranges with step.

For Arrays it is easy, I just go over each element and convert negative integers to appropriate values before matching.

  1. Separately, Crystal supports creating empty ranges, such as 10..-1 or (10..-1).step 2.

To have the functionality be uniform, in my comparison functions if I detect that r.end is negative, I know that the user actually wanted to count from the end, so I convert e.g. a range like 10..-1 into 10..31.

I can do this since I can query Range#begin and #end. When step is involved, the object is different and I cannot query it in the same way to determine its settings.

docelic avatar Dec 18 '23 20:12 docelic

@straight-shoota It actually seems this iterator type is public. So we may be stuck with supporting use cases related to this specific type.

https://crystal-lang.org/api/Steppable/StepIterator.html

Blacksmoke16 avatar Dec 18 '23 21:12 Blacksmoke16

Steppable::StepIterator seems to be a rare outlier then. Range::StepIterator is actually private.

straight-shoota avatar Dec 19 '23 11:12 straight-shoota

/me wishing how nice would be to have the step as a part of the Range literal

Sija avatar Dec 23 '23 01:12 Sija