C-style For Loops

I think that the C-style for loop should be removed from Swift. The scope rules for this for loop are wrong. Every loop sees the same scope. This is a source of bugs if you export the loop variable name outside the scope of the for statement, for example in a closure. The following code illustrates the problem:

var handlers: [() -> ()] =

for i in 0..<5 {
    handlers.append { print(i) }
}

for handler in handlers {
    handler() // "0 1 2 3 4"
}

handlers =

for var i = 0; i < 5; i += 1 {
    handlers.append { print(i) }
}

for handler in handlers {
    handler() // "5 5 5 5 5"
}

The Swift for-in loop does the right thing naturally. The C-style for loop does the wrong thing naturally. Removing the C-style for loop from Swift will eliminate one more class of possible errors from the language.

I think that the C-style for loop should be removed from Swift.

The silence you’re hearing on this proposal is because we just had a thread about this a couple days ago. :^) Take a look at the archives--most people seem to agree that the for loop ought to go. They’ve even started changing standard library for loops to for-in.

···

--
Brent Royal-Gordon
Architechies

The patch that was submitted changes the obvious cases. It would be
good to see a patch that converts all instances, including the "hard"
ones.

Dmitri

···

On Sun, Dec 6, 2015 at 3:22 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

They’ve even started changing standard library for loops to for-in.

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

Hi Reviewers,

Since it looks like Nate has already done that analysis for the standard library, let’s analyze Dmitri's semi-obfuscated prime signature for fun.

Starting with:

main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

First transform into Swift. (Using += 1 instead of ++ since we know those are going.)

var j: Int
for var i=2; ; i+=1 {
  for j=2; j<i; j+=1 {
    if i%j == 0 {
      j = 0
      break
    }
  }
  if j != 0 {
    print(i)
  }
} // Dmitri Gribenko <gribozavr@gmail.com>

Next get rid of those for loops.

for i in 2 ..< Int.max {
  var j = 2
  while j < i {
    if i%j == 0 {
      j = 0
      break
    }
    j += 1
  }
  if j != 0 {
    print(i)
  }
} // Dmitri Gribenko <gribozavr@gmail.com>

There is an improvement in that it forced us to avoid integer overflow by specifying the end game (although the heat death of the universe might happen first if you run this in a playground). Since j escapes the scope of it’s loop we couldn’t use a for statement variable. I think this is actually a good thing because it makes this wrinkle more apparent.

For-in almost always looks better. If things get too complicated you can always fall back to a different control structure like while or repeat. BTW, looking at Doug’s implementation of the Sieve of Eratosthenes from his WWDC Value-Types talk, it uses for-in-stride. He explicitly preferred that to a traditional C-style even though it would have been easy to go either way. It just looks better. Fewer semicolons, it must be good.

Here is my review:

1. I endorse the proposal
2. I think it is worthwhile as it will force people to adopt good practices early and break bad habits.
3. I believe the change goes along well with the removal of ++ and — operator. Reducing the surface area of the language is a good thing.
4. It will cause the “Swift is not stable!” crowd to go a little more crazy but I am okay with that.
5. I thought about this pretty hard, checked my usage of for loops in a non-trivial code base. I feel even more comfortable after Nate’s analysis of the standard library.

PS: I do not recommend any changes to Dmitri's signature. :)

You can use a for loop, you just have to use a flag to indicate whether the number was found to be non-prime, or a label. Label approach (since using a flag is obvious);

candidate: for i in 2 ..< Int.max {
    for j in 2 ..< i {
        if i % j == 0 {
           continue candidate
        }
    }
    print(i)
}

-DW

···

On Dec 7, 2015, at 4:58 PM, Ray Fix via swift-evolution <swift-evolution@swift.org> wrote:
There is an improvement in that it forced us to avoid integer overflow by specifying the end game (although the heat death of the universe might happen first if you run this in a playground). Since j escapes the scope of it’s loop we couldn’t use a for statement variable. I think this is actually a good thing because it makes this wrinkle more apparent.