Review for: Remove C-style for-loops with conditions and incrementers

Review of SE-0007

Note: haven't had time to write this up during the week, apologies for
the late comment.

  * What is your evaluation of the proposal?

+1 on the proposal, as long as my suggestion below ("repeat" clause) is
also implemented.

I always felt the C-style `for var i = 0; i < n; i += 1 {...}` loop was
an anachronism. A semicolon-separated list of heterogeneous items:
- a declaration and/or initialization;
- a boolean expression;
- a statement which is executed not there, but between loop iterations.

I do remember in the early C days that (some) people delighted in
cramming stuff inside the for header and have an empty loop body. This
was fun, but needlessly obscure/tricky. Enumerating a linked list was a
frequent sample.

Also, for many purposes one had to use tricks, like extra booleans, or a
switch on the index, to special-handle the first or last time through
the loop.

Substituting a `while` loop can be, as many have already pointed out,
seriously problematic when `break`, `continue` and `throw` are used
inside the loop, and `defer` is not a good solution for that, either.

So, I propose that the `while` loop should get an optional `repeat`
clause at the end, like this:

var item = firstItem // declaration/initialization
while item != nil { // condition
  if SomeExtraCondition(item) {
    break;
  }
  process(item)
} repeat { // increment
  item = NextItemFor(item)
}

Where the statements inside `repeat` are executed after each loop, but
only if the loop will continue. I think this offers the necessary
conceptual clearness and existing for-loops are easily converted
automatically. The eye will skip over the `repeat` clause for the last
iteration as it now does for `else`.

Also, `break`, `continue` and `throw` will all continue to work as expected.

I also propose that the `repeat` clause be extended to the remaining
`for-in` loop:

for item in sequence {
  print("\(item)")
} repeat {
  print(", ")
}

which will be handy in many situations.

Finally, I think that `for i in 0..<n` is acceptable for counting.
Having reverse ranges would help but is not as frequent.

BTW: `loop` or whatever is felt to be more readable could be used in
place of `repeat`.

  * Is the problem being addressed significant enough to warrant a change
to Swift?

Yes, I do think that it will remove a source of errors for programmers
migrating in from C/C++/ObjC, and the "repeat" clause will offer a more
functionality for the loops.

  * Does this proposal fit well with the feel and direction of Swift?

Yes.

  * If you have you used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?

I've used C/ObjC extensively for decades, older languages are probably
not useful by now :-)

  * How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

I've read (and now, re-read most comments on this list. I haven't
checked the Swift sources for implementation implications (nor do I feel
yet qualified to do so).

···

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."

So, I propose that the `while` loop should get an optional `repeat`
clause at the end, like this:

var item = firstItem // declaration/initialization
while item != nil { // condition
  if SomeExtraCondition(item) {
    break;
  }
  process(item)
} repeat { // increment
  item = NextItemFor(item)
}

Where the statements inside `repeat` are executed after each loop, but
only if the loop will continue. I think this offers the necessary
conceptual clearness and existing for-loops are easily converted
automatically. The eye will skip over the `repeat` clause for the last
iteration as it now does for `else`.

Also, `break`, `continue` and `throw` will all continue to work as expected.

I also propose that the `repeat` clause be extended to the remaining
`for-in` loop:

for item in sequence {
  print("\(item)")
} repeat {
  print(", ")
}

which will be handy in many situations.

I haven’t had time to do a thorough review of the proposal myself but I have read the proposal and have been following the responses. My impression is that most people who don’t support this proposal would do so if there was an acceptable alternative which retains the performance characteristics of the C-style for loop.

Rainer’s proposal looks like a good way to provide an alternative that addresses semantic and performance concerns while also providing a tool that generalizes to all loops. It feels “Swifty” and seems like a win to me.

I can’t think of any reason to oppose the proposal with Ranier’s solution other than old habits, familiarity from other languages, etc.

I do think we need to consider whether “repeat” is the best keyword due to potential confusion with "repeat while” loops. The advantage of using it is avoiding adding a new keyword. The disadvantage is overloading its meaning.

Matthew

Are you sure this does what you expect?

···

Le 12 déc. 2015 à 9:41, Rainer Brockerhoff via swift-evolution <swift-evolution@swift.org> a écrit :

I also propose that the `repeat` clause be extended to the remaining
`for-in` loop:

for item in sequence {
  print("\(item)")
} repeat {
  print(", ")
}

which will be handy in many situations.

--
Michel Fortin
michel.fortin@michelf.ca
https://michelf.ca

Ouch. I had a vague idea I was overlooking some use of `repeat`, and
right, I'd forgotten about that usage. But other keywords would do, as I
said.

···

On 12/12/15 13:44, Matthew Johnson wrote:

I do think we need to consider whether “repeat” is the best keyword
due to potential confusion with "repeat while” loops. The advantage
of using it is avoiding adding a new keyword. The disadvantage is
overloading its meaning.

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."

Apparently Swift 2.x isn't properly in my brain yet, should've added
`terminator:""` to those prints. Or thought of a better example.

Or added more coffee... :-)

···

On 12/12/15 15:00, Michel Fortin wrote:

Le 12 déc. 2015 à 9:41, Rainer Brockerhoff via swift-evolution <swift-evolution@swift.org> a écrit :

I also propose that the `repeat` clause be extended to the remaining
`for-in` loop:

for item in sequence {
  print("\(item)")
} repeat {
  print(", ")
}

which will be handy in many situations.

Are you sure this does what you expect?

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."

True. But that's not what I meant here. Your example produces the same result as this one:

  for item in sequence {
    print("\(item)")
    print(", ")
  }

Since there is no `continue` in the loop body, there's no point in having a `repeat` block.

My guess is that your intent was to have ", " between each item, but not after the last one. That's not how the increment block works in a C-style for loop. Try this loop if you want to convince yourself:

  for var i = 0; i < 5; i += 1, print(", ", terminator: "") {
    print(i, terminator: "")
  }

Prints:

  0, 1, 2, 3, 4,

···

Le 12 déc. 2015 à 13:22, Rainer Brockerhoff <rainer@brockerhoff.net> a écrit :

On 12/12/15 15:00, Michel Fortin wrote:

Le 12 déc. 2015 à 9:41, Rainer Brockerhoff via swift-evolution <swift-evolution@swift.org> a écrit :

I also propose that the `repeat` clause be extended to the remaining
`for-in` loop:

for item in sequence {
  print("\(item)")
} repeat {
  print(", ")
}

which will be handy in many situations.

Are you sure this does what you expect?

Apparently Swift 2.x isn't properly in my brain yet, should've added
`terminator:""` to those prints. Or thought of a better example.

--
Michel Fortin
michel.fortin@michelf.ca
https://michelf.ca

My guess is that your intent was to have ", " between each item, but

not after the last one. That's not how the increment block works in a
C-style for loop. Try this loop if you want to convince yourself:

  for var i = 0; i < 5; i += 1, print(", ", terminator: "") {
    print(i, terminator: "")
  }

Yikes. This is why I dislike the C-stye for loop; I never remember such
details properly, and avoid putting multiple items in there. (Same goes
for function pointer syntax and ObjC block pointer syntax, nested
typedefs, etc.)

Offhand (can't test right now) I suppose that means there's no use in
adding `repeat` (or, `loop`, or whatever) to `for ... in`?

Would you say it'd still be useful for the `while` loop?

···

On 12/12/15 16:30, Michel Fortin wrote:

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."

Offhand (can't test right now) I suppose that means there's no use in
adding `repeat` (or, `loop`, or whatever) to `for ... in`?

It could simplify for … in loops where there are early continue statements if there is also some logic that needs to be executed following *every* iteration, including those exited by a continue statement. I’m not sure how common examples of this would be but they probably exist. Where such logic exists the ‘repeat’ / ‘loop’ / etc construct would simplify it and make it less prone to error especially over time as maintenance is performed on the code.

If we introduce a construct like this we could also introduce a companion ‘between’ (keyword doesn’t really matter) which *would* do exactly what you intended.

for item in sequence {
  print("\(item)")
} between {
  print(", ")
}

I don't think it's particularly intuitive that the two consecutive blocks separated by a keyword are part of the same loop body in the first place. Somebody is prone to do the same mistake you did with a while loop, although maybe it's less likely (I don't really know).

It'd certainly be useful if the C-style for loop disappear to have a decent way to express it using a while loop, but I'm not sure this is the way to go. As things stand, we have the following ideas about augmenting a while loop (with various keywords I won't enumerate):

  while condition() next increment() {
    body()
  }

  while condition() {
    body()
  } repeat {
    increment()
  }

Perhaps we could consider adding something like `defer` but that would not execute on `break` or `throw`:

  while condition() {
    reloop { increment() }
    body()
  }

Or maybe just make the whole C-style for loop a special kind of while loop by replacing semicolons with keywords:

  for var i = 0 while condition() next increment() {
    body()
  }

There's so many ideas floating around, but it doesn't seem like any of them is getting much traction right now.

My personal preference goes to this last one that essentially keeps the C-style for loop but with keywords. It's easily recognizable to those who already know the C-style for loop, easier to understand to those who don't, and it does not force all C-style for loops to be rewritten using a different control flow.

Moreover, if you accompany this change with a fix-it having a special case that migrates the old forms "for;;" and "for(;;)" by proposing the appropriate range-based for-in loop as a replacement for simple forward iteration over integers, beginners writing "for;;" will likely switch to using the for-in form after being shown the way once or twice.

···

Le 12 déc. 2015 à 13:42, Rainer Brockerhoff <rainer@brockerhoff.net> a écrit :

Yikes. This is why I dislike the C-stye for loop; I never remember such
details properly, and avoid putting multiple items in there. (Same goes
for function pointer syntax and ObjC block pointer syntax, nested
typedefs, etc.)

Offhand (can't test right now) I suppose that means there's no use in
adding `repeat` (or, `loop`, or whatever) to `for ... in`?

Would you say it'd still be useful for the `while` loop?

--
Michel Fortin
michel.fortin@michelf.ca
https://michelf.ca

Thanks for the round-up. You're right.

I'll think this over and re-read the grammar before commenting further.

···

On 12/12/15 17:48, Michel Fortin wrote:

It'd certainly be useful if the C-style for loop disappear to have a
decent way to express it using a while loop, but I'm not sure this is
the way to go. As things stand, we have the following ideas about
augmenting a while loop (with various keywords I won't enumerate):

while condition() next increment() { body() }

while condition() { body() } repeat { increment() }

Perhaps we could consider adding something like `defer` but that
would not execute on `break` or `throw`:

while condition() { reloop { increment() } body() }

Or maybe just make the whole C-style for loop a special kind of while
loop by replacing semicolons with keywords:

for var i = 0 while condition() next increment() { body() }

There's so many ideas floating around, but it doesn't seem like any
of them is getting much traction right now.

--
Rainer Brockerhoff <rainer@brockerhoff.net>
Belo Horizonte, Brazil
"In the affairs of others even fools are wise
In their own business even sages err."