Milos, you make good points. This thread is really long and hard to follow, so I'll reply inline below with some observations that have been made in the past, which I think address some of them. See if you like where things are headed.
On Sat, Apr 2, 2016 at 8:05 PM, Milos Rankovic via swift-evolution <swift-evolution@swift.org> wrote: > `Strideable` types represent an often needed generalisation of `Range` and > `IntervalType`s. However, `Strideable`’s two `stride` methods are far too > verbose and unbalanced (in contrast to the natural look and feel of the two > interval operators). Examples like the following raise a number of issues: > > 1.stride(through: 5, by: 2) // 1, 3, 5 > > 1.stride(through: 5, by: -2) // [] > > 1. The method's verbosity keeps the bounds too far apart. > > 2. The dot syntax suggests that something is being done to the start bound, > with the end bound playing the role of an argument, all of which does not > really reflect the semantics of the call. An older syntax is being restored in Swift 3: `stride(from: 1, to: 5, by: 2)` and `stride(from: 1, through: 5, by: 2)`, and dot syntax is being removed. Bounds are now next to each other, and the start and end values are now visually equals. > 3. The direction in which we advance from one end to another of the interval > is provided twice: once by the order of the bounds and then again by the > sign of the stride argument. The stride direction is strictly given by the sign of the last argument; `stride(from: 1, to: -5, by: 2)` is an empty sequence, because you cannot get from start to end by -2. See next comment for why I think this is a feature, not a bug. > 4. Given the conceptual proximity of `Strideable`, `IntervalType` and > `Range`, one would expect analogous ways of constructing them. > > 5. The word “stride” is not particularly friendly to programmers whose first > language is not English (again in contrast to the interval operators). This > is compounded by the distinction between `to` and `through` parameters. > > As already noted in this thread, we could simply extend the existing types: > > extension ClosedInterval where Bound : Strideable { > func by(stride: Bound.Stride) -> StrideThrough<Bound> { > let (s, e) = stride < 0 ? (end, start) : (start, end) > return s.stride(through: e, by: stride) > } > } > > extension HalfOpenInterval where Bound : Strideable { > func by(stride: Bound.Stride) -> StrideTo<Bound> { > let (s, e) = stride < 0 ? (end, start) : (start, end) > return s.stride(to: e, by: stride) > } > } > > So that: > > (1...5).by(2) // 1, 3, 5 > (1..<5).by(2) // 1, 3 > > (1...5).by(-2) // 5, 3, 1 > (1..<5).by(-2) // 5, 3 Yes, I do think that's a great idea, as do other people! Because Dave A is making some big changes to Range (and Intervals are going away, leaving only Range), I haven't tried to extend Range in my last proof-of-concept, but I think there's momentum to add a `striding(by:)` method to Range to do exactly that, `striding(by:)` being more clear than `by(_:)`. One difference between `Range.striding(by:)` and `stride(from:to:by:)` will be that it's a fatal error to try to construct `1..<(-5)` as a Range, but if you read the comments in the code for StrideTo, the original designers of stride explicitly wanted `stride(from: 1, to: -5, by: 1)` to be allowed. When you can't get from start to end by the chosen stride, the result is an empty sequence instead of a fatal error. There may be use cases where that behavior is preferred, so I'm in favor of adding `striding(by:)` to Range but also keeping `stride(...)`. > More exotically, we could make use of subscripts: > > extension ClosedInterval where Bound : Strideable { > subscript(stride: Bound.Stride) -> StrideThrough<Bound> { > return by(stride) > } > } > > extension HalfOpenInterval where Bound : Strideable { > subscript(stride: Bound.Stride) -> StrideTo<Bound> { > return by(stride) > } > } > > (1...5)[-2] // 5, 3, 1 > > Or introduce a new, or overload an existing operator, with precedence just > lower than the two interval operators. For example: > > func > <T> (i: ClosedInterval<T>, stride: T.Stride) -> StrideThrough<T> > { > return i.start.stride(through: i.end, by: stride) > } > > func < <T> (i: ClosedInterval<T>, stride: T.Stride) -> StrideThrough<T> > { > return i.end.stride(through: i.start, by: -stride) > } > > func > <T> (i: HalfOpenInterval<T>, stride: T.Stride) -> StrideTo<T> { > return i.start.stride(to: i.end, by: stride) > } > > func < <T> (i: HalfOpenInterval<T>, stride: T.Stride) -> StrideTo<T> { > return i.end.stride(to: i.start, by: -stride) > } > > for i in 1...5 < 2 { > i // 5, 3, 1 > } > > for i in 1...5 > 2 { > i // 1, 3, 5 > } I've suggested something like that to be possible earlier in the thread; didn't get too much of a positive reception. People seem to like `by(_:)` or `striding(by:)` though. > > Not to mention a C-style `for` loop lookalike: > > for i in (1 to 5 by 2) { > i // 1, 3, 5 > } > > Obviously, this whole thread is related to the C-style `for` loop (which is > more general than all of the above solutions) as well as to Haskell-style > list comprehension syntax (which remains enviable). Nevertheless, I do think > that a focused, lightweight feature would be the best fit for such a common > need (just think, for example, how often are such sequences used for > instructional purposes). > > One other possibility is to introduce open-ended, infinite sequences defined > by a single bound and a stride: > > // infinite sequence, starting with 5 and advancing by -2 > (5..|-2) > > … which could be optionally closed by one of the interval operators: > > (5..|-2)...1 > > I’ve read somewhere that the “interval is going away”, in which case, a new > tertiary operator may be worth considering since striding is such a > fundamental operation. Or really any of the above – just not sticking to the > existing `stride` methods! > > milos > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution > _______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution