[swift-evolution] Feature proposal: Range operator with step

Xiaodi Wu xiaodi.wu at gmail.com
Wed Apr 6 15:09:18 CDT 2016


On Wed, Apr 6, 2016 at 1:16 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>
> on Wed Apr 06 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:
>
>> I think a lightbulb just went on for me:
>>
>> You're talking about expressing something in the vein of `(0..<200).striding(by:
>> -2)`, which has I'm sure many use cases, and which isn't straightforward to
>> express with the current free function--I hadn't considered that.
>>
>> Meanwhile, I was trying to talk about something like `stride(from: 200, to: 0,
>> by: -2)`, which is easily expressed today but isn't straightforward at all to
>> preserve with only ranges. Clearly, given that this is what's on offer
>> currently, someone who designed the language thinks (or thought) it's of some
>> use.
>
> That someone was me, and I explained that it wasn't an extremely
> deeply-considered decision.

Fair enough. Though your decision may not have been deeply considered,
I wouldn't say it was an ill-considered one given that there wasn't
(to my knowledge) any great clamor against it subsequently.


>> In the absence of information as to which is more in demand, couldn't
>> we have both?
>
> That's not how we make decisions about what should be in the language or
> standard library.  We need to make choices based on (at least educated
> guesses about) what people need, or we'll end up with a sprawling mess.

Well, to elicit the kind of feedback that would help determine user
needs, I would suggest that (when the eventually reconsidered syntax
is proposed) this change should be highlighted explicitly as a feature
removal. IMO, it wouldn't be otherwise immediately apparent from a
quick glance that revising `stride(from: 0, to: 10, by: 1)` to
`(0..<10).striding(by: 1)` necessarily entails deletion of backwards
strides from upper bound to-and-not-through lower bound.


>> If it must be a method on a range,
>
> It's not that it must be, but having such a method tends to reduce API
> surface area.  We prefer methods to free functions.
>
>> then I would advocate for having what seems to be an utterly
>> reasonable set of options for striding backwards:
>>
>> ```
>> (0...200).striding(by: -2) // [a, b]
>> (0..<200).striding(by: -2) // [a, b)
>> (0<..200).striding(by: -2) // (a, b]
>> ```
>
> And I'm trying to say that without a more compelling reason to introduce
> `<..`, I don't want to do it.  I'd like to know that `<..` is useful
> outside the domain of striding, for example.  Use-cases, anyone?

Well, my use case (an actual one) is supremely mundane. I'm doing some
scientific computing and I need to deal with numeric intervals. Some
of them are closed, some of them are open at one end, and some at the
other. I'd like to be able to represent any of those as
Intervals-which-are-now-Ranges. It makes sense to do so because the
things I want to do with them, such as clamping and testing if some
value is contained, are exactly what Intervals-now-Ranges provide.
Looking around, it seems many other languages provide only what Swift
currently does, but Perl does provide `..`, `..^`, `^..`, and `^..^`
(which, brought over to Swift, would be `...`, `..<`, `<..`, and
`<.<`).


> Reasons we might not need it: the cases where it's important are much
> more likely to be notionally continuous domains (e.g. floats), since you
> can always write
>
>     ((0+1)...200).striding(by: -2)
>
> and if you need the floating version there's always
>
>    (0.0...200.0).striding(by: -2).lazy.filter { $0 != 0.0 }
>
> which probably optimizes down to the same code.
>
> One question that I *do* think we should answer, is whether the elements
> of
>
>     (0..<199).striding(by: -2)
>
> are even or odd.
>
>> On Wed, Apr 6, 2016 at 12:10 PM Dave Abrahams via swift-evolution
>> <swift-evolution at swift.org> wrote:
>>
>>     on Wed Apr 06 2016, Brent Royal-Gordon <brent-AT-architechies.com> wrote:
>>
>>     >> For example, there are all kinds of other ways to slice this:
>>     >>
>>     >> stride(over: 0..<200, by: -2)
>>     >
>>     > This seems like a particularly good solution. The way I understand it
>>     > at least, it would allow ranges to always be ordered, with the only
>>     > difference being whether it went start-to-end or end-to-start,
>>     > determined by the stride's sign.
>>
>>     This is no different in principle from
>>
>>     (0..<200).striding(by: -2)
>>
>>     Again, I wasn't trying to suggest any of the solutions listed there.
>>     The point I was making was that we don't have enough information to
>>     design more than
>>
>>     (0..<200).striding(by: -2)
>>
>>     > It would also avoid the need for additional range operators. The main
>>     > reason you would need `>..` is so you could say
>>     > `array.endIndex>..array.startIndex`, but by using the sign to decide
>>     > which direction to stride over the range, you instead stride over
>>     > `array.startIndex..<array.endIndex`, which is exactly what we already
>>     > have.
>>     >
>>     > Unfortunately, moving away from `stride(from:to/through:by:)` would
>>     > kind of mess up an idea I've been developing for providing an
>>     > "induction sequence" to replace the more complicated C-style for use
>>     > cases, but I suppose that's the way it goes...
>>     >
>>     > (Link to that:
>>     https://gist.github.com/brentdax/b24dd89a770d9fe376984498d3185187)
>>
>>     --
>>     Dave
>>     _______________________________________________
>>     swift-evolution mailing list
>>     swift-evolution at swift.org
>>     https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
> --
> Dave


More information about the swift-evolution mailing list