[swift-evolution] [Draft]: Introducing a striding(by:) method on 3.0 ranges

Xiaodi Wu xiaodi.wu at gmail.com
Sat Apr 9 18:39:00 CDT 2016


IMO, 9...0 is a non-starter. Especially with Range taking on the role of
Interval, it's critical that they have no direction. Suppose you have two
ranges, 0...9 and 9...0. Is 0...9 == 9...0? IMO, two countable ranges r0
and r1 are equal if, for value in r0, r1.contains(value) == true, and vice
versa. (Sorry for the pseudocode; I'm typing on a phone.) However, if Range
has intrinsic direction, either equality ignores direction (ludicrous) or
0...9 != 9...0 (unacceptable).

Since we have `reverse()`, all ambiguity could be resolved by prohibiting
negative stride sizes altogether, I suppose. So, (0...9).striding(by:
2).reverse() could be the one Swifty way to stride backwards. Is that
objectionable to people?

On Sat, Apr 9, 2016 at 11:58 PM Haravikk <swift-evolution at haravikk.me>
wrote:

> On 9 Apr 2016, at 10:50, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> But, ranges are useful for more than just stride, and so I would say it
> doesn't make sense to have an intrinsic direction for Range, which
> pointedly has lowerBound and upperBound but not start and end. For sample,
> there shouldn't be (0...9).contains(1) and (9...0).contains(1).
>
>
> While I can appreciate that, I wonder if it’s really that important that
> 0…9 and 9…0 are identical? As long as we can be clear on which direction of
> range we want to generate (so probably still need two new operators), can
> tell what the direction is, and can convert between them if we need to for
> some reason (reverse should be fine for that?), then I think we’re okay.
>
> i.e- 9 … 0 would still cause an error at compile or run-time, we’d have
> some other operator for doing that, not sure what, plus 9 ..> 0, with both
> explicitly creating ranges in the reverse direction to avoid mistakes with
> the order or for computed indices. When it comes down to it the current
> Range has an implicitly forward direction, so I don’t see the problem with
> having the same in reverse personally.
>
>
> I dunno, at the very least we might want to consider overloading the
> striding method as .striding(forwardBy:) and .striding(backwardBy:) or
> something similar, each taking a positive value of some kind to help avoid
> mistakes in cases where the stride size is computed rather than constant,
> this would make it more explicit at least.
>
> On Sat, Apr 9, 2016 at 10:33 AM Haravikk <swift-evolution at haravikk.me>
> wrote:
>
>> While I’m in favour of the basic idea I think the operator selection is
>> too complex, and I’m not sure about the need for negative strides. Really
>> all I want are the following:
>>
>> (0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
>> (0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
>> (6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
>> (6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0
>>
>> Everything else should be coverable either by flipping the order, or
>> using .reverse(). The main advantage is that there’s only one new operator
>> to clarify the 6 ..> 0 case, though you could always just reuse the
>> existing operator if you just interpret it as “x from 6 to, but not
>> including, 0"
>>
>> I dunno, I just don’t think that introducing tons of new operators is
>> going to simplify things, and could lead to way more mistakes in practice;
>> the only mistake above would be putting the indices in the wrong order and
>> accidentally reversing the result.
>>
>> Also, I’m against negative strides; while they could be useful for
>> convenience (avoid the need for .reverse or flipping values) I’m just not
>> sure that it makes sense. To me a stride is a distance, thus absolute, what
>> matters is the direction of the range. Naturally we’d need ranges with a
>> direction, but with collections requiring it to be in a particular order
>> (or ignoring it and flipping where necessary).
>>
>> On 8 Apr 2016, at 19:37, Erica Sadun via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Draft here:
>> https://gist.github.com/erica/a51a981ee0352235204692affa959307  Feedback
>> solicited, both positive and negative.
>> We've also got a related proposal about expanding ranges, which you can
>> look at here (
>> https://gist.github.com/erica/af92c541a0fb69fce1b7aaf8374a5aa9)
>>  but we want to float this one first.
>>
>> Thanks, -- E
>>
>>
>>
>>
>>    - Proposal: SE-XXXX
>>    <https://gist.github.com/erica/a51a981ee0352235204692affa959307/edit>
>>    - Author(s): Xiaodi Wu <https://github.com/xwu>, Pyry Jahkola
>>    <http://github.com/pyrtsa>, Nate Cook <http://github.com/natecook1000>
>>    , Erica Sadun <http://github.com/erica>
>>    - Status: TBD
>>    - Review manager: TBD
>>
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#introduction>
>> Introduction
>>
>> We propose to introduce a striding(by:) method on the revised 3.0 Range
>>  type.
>>
>> This proposal was discussed on the Swift Evolution list in the Feature
>> proposal: Range operator with step
>> <http://search.gmane.org/search.php?group=gmane.comp.lang.swift.evolution&query=Feature+proposal%3A+Range+operator+with+step> thread.
>> (Direct link
>> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/12801/focus=13051> to
>> original thread)
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#motivation>
>> Motivation
>>
>> Updating Range for Swift 3 offers a window of opportunity to
>> simultaneously improve strides.
>>
>>    -
>>
>>    Under current Swift 3 plans, n.stride(to:/through:, by:) will be
>>    replaced with a standalone stride(from:, to:/through:, by:) function.
>>    We propose to replace this change with a method on ranges. Using a method
>>    reduces overall API surface area compared to free functions.
>>    -
>>
>>    In its current incarnation, the standalone stride function uses
>>    confusing semantics. The current to implementation returns values in
>>    *[start, end)* and will never reach or get *to* end. The current
>>    through implementation returns values in *[start, end]*. It may never
>>    reach end and certainly never goes *through* that value. Our proposed
>>    method introduces simple, expected semantics that can be extended to both
>>    countable and continuous ranges, and to open and closed intervals (both
>>    half-open and fully-open).
>>
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#detail-design>Detail
>> Design
>>
>> The striding(by:) method is called on ranges. When used with a positive
>> step size, the count starts from the lower bound. With a negative step
>> size, the count starts from the upper bound. These bounds apply regardless
>> of whether they are inclusive or exclusive.
>>
>> The following examples should cover all corner cases and include possible
>> cases should Swift 3 introduce a full complement of open and closed ranges.
>> The syntax for non-canonical range types is not fixed and can be discussed
>> under separate cover.
>>
>> (0 ... 9).striding(by: 2) == [0, 2, 4, 6, 8]
>> (0 ..< 9).striding(by: 2) == [0, 2, 4, 6, 8]
>> (0 <.. 9).striding(by: 2) ==    [2, 4, 6, 8]
>> (0 <.< 9).striding(by: 2) ==    [2, 4, 6, 8]
>>
>> (0 ... 9).striding(by: 3) == [0, 3, 6, 9]
>> (0 ..< 9).striding(by: 3) == [0, 3, 6]
>> (0 <.. 9).striding(by: 3) ==    [3, 6, 9]
>> (0 <.< 9).striding(by: 3) ==    [3, 6]
>>
>> (0 ... 9).striding(by: -2) == [9, 7, 5, 3, 1]
>> (0 ..< 9).striding(by: -2) ==    [7, 5, 3, 1]
>> (0 <.. 9).striding(by: -2) == [9, 7, 5, 3, 1]
>> (0 <.< 9).striding(by: -2) ==    [7, 5, 3, 1]
>>
>> (0 ... 9).striding(by: -3) == [9, 6, 3, 0]
>> (0 ..< 9).striding(by: -3) ==    [6, 3, 0]
>> (0 <.. 9).striding(by: -3) == [9, 6, 3]
>> (0 <.< 9).striding(by: -3) ==    [6, 3]
>>
>> To reverse a stride, call reverse() on the results:
>>
>> (0 ... 9).striding(by: 2).reverse() == [8, 6, 4, 2, 0]
>>
>> We note that striding by 0 should be always be a precondition failure.
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#alternatives-considered>Alternatives
>> Considered
>>
>> During the on-list discussion, we considered various scenarios that took
>> closed/inclusive bounds into account or excluded open bounds for starting
>> values. For example, we might have prohibited scenarios where multiple
>> interpretations of an intended behavior might exist: is (0 ..<
>> 9).striding(by: -2) a precondition failure? We settled on the simplest,
>> most straight-forward implementation involving the fewest compiler warnings
>> and the lowest likelihood of precondition failures. We subscribe to the
>> "Dave Abrahams Philosophy": excessive special casing and warning scenarios
>> more likely indicates bad language design than bad user comprehension.
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#future-directions>Future
>> Directions
>>
>> We intend to follow up with an expanded operator vocabulary that includes
>> fully open ranges (<.<), fully closed ranges (...) and both half open
>> ranges (<.., ..<). These will support the full vocabulary laid out in
>> the Detail Design section.
>>
>> Upon adoption, the Swift community may consider expanding this approach
>> to collection indices, for example:
>>
>> let a = [8, 6, 7, 5, 3, 0, 9]
>> for e in a.striding(by: 3) {
>>     print(e) // 8, then 5, then 9
>> }
>>
>> Striding offers a fundamental operation over collections. The consistent
>> approach introduced in this proposal
>> <http://article.gmane.org/gmane.comp.lang.swift.evolution/13936> helps
>> support the extension of stride semantics to collections.
>>
>> <https://gist.github.com/erica/a51a981ee0352235204692affa959307#acknowlegements>
>> Acknowlegements
>> Thanks, Dave Abrahams, Matthew Judge
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160409/d2ad9a95/attachment.html>


More information about the swift-evolution mailing list