[swift-dev] C-style For Loops

Dave Abrahams dabrahams at apple.com
Sat Dec 19 16:00:57 CST 2015


> On Dec 19, 2015, at 1:47 PM, Brent Royal-Gordon <brent at architechies.com> wrote:
> 
>>> for lat in (CGFloat(-60)...60).by(30) {
>>>  print(lat)
>>> }
>>> 
>>> for lat in (CGFloat(-60)..<60).by(30) {
>>>  print(lat)
>>> }
>> 
>> This is nice; why don't we put it in the standard library and get rid of the "stride" methods?
> 
> One practical reason is that interval.start must always be before interval.end, so this makes striding in reverse difficult.

We could declare that negative strides cause us to start at the end rather than at the start.

> Another is that the precedence of the interval operators forces additional parentheses, so the resulting code is usually ugly.

I'm aware of that, but it seems less ugly to me than the alternative, and more importantly it keeps the range/interval EDSL more integrated.

> (And making them higher-precedence than member access has its own problems, like `0..<(foo.count)`.)
> 
> Honestly, as much as we try to avoid them, I think the old free functions in Swift 1 gave us the best-looking code. Adapted to use intervals:
> 
>     for lat in stride(CGFloat(-60)…60, by: 30) {
>          print(lat)
>    }
> 
> Or, a little more cleanly:
> 
>     for lat: CGFloat in stride(-60…60, by: 30) {
>          print(lat)
>    }
> If you don’t like the function syntax, one funky alternative might be to overload `+`:
> 
>     for lat: CGFloat in -60...60 + 30 {	// Meaning “increment by 30 each time"
>          print(lat)
>    }
> 
> Hmm. Rather than overloading, maybe we can use two recently-freed-up operators which are traditionally associated with looping...
> 
>    infix operator ++ { associativity none precedence 130 }	// 130 is looser than the interval operators
>    infix operator -- { associativity none precedence 130 }
> 
>    func ++ <Bound: Strideable>(interval: ClosedInterval<Bound>, stride: Bound.Stride) -> StrideThrough<Bound> {
>        // These implementations are just how they work against the current public API, of course.
>        return interval.start.stride(through: interval.end, by: stride)
>    }
> 
>    func ++ <Bound: Strideable>(interval: HalfOpenInterval<Bound>, stride: Bound.Stride) -> StrideTo<Bound> {
>        return interval.start.stride(to: interval.end, by: stride)
>    }
> 
>    func -- <Bound: Strideable>(interval: ClosedInterval<Bound>, stride: Bound.Stride) -> StrideThrough<Bound> {
>        return interval.end.stride(through: interval.start, by: -stride)
>    }
> 
>    func -- <Bound: Strideable>(interval: HalfOpenInterval<Bound>, stride: Bound.Stride) -> StrideTo<Bound> {
>        return interval.end.stride(to: interval.start, by: -stride)
>    }
> 
> Giving you:
> 
>    // Counts up from start by 30
>    for lat: CGFloat in -60...60 ++ 30 {
>        print(lat)
>    }
> 
>    // Counts down from end by 30
>    for lat: CGFloat in -60...60 -- 30 {
>        print(lat)
>    }
> 
> Perhaps this could even be extended to take a Bound -> Bound function as the right-hand parameter, allowing for the sort of arbitrary incrementing that some complain we've lost without C-style for:
> 
>    for lat: CGFloat in 1...60 ++ { $0 * 2 } {
>        print(lat)
>    }
> 
> And, of course, we could also provide unary variants which do +1 and -1:
> 
>    // Counts up from start by 1
>    for lat: CGFloat in -60...60++ {
>        print(lat)
>    }
> 
>    // Counts down from end by 1
>    for lat: CGFloat in -60...60-- {
>        print(lat)
>    }


None of those syntaxes fly for me, I'm afraid.  They all look like line noise (and some have precedence problems); I much prefer requiring a set of parentheses.

-Dave





More information about the swift-dev mailing list