`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.

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.

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

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
    }

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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to