[swift-evolution] C-style For Loops

Erica Sadun erica at ericasadun.com
Sun Dec 6 10:59:14 CST 2015


Last week, I made the following proposal for Swift:

The C-style for-loop appears to be a mechanical carry-over from C rather than a genuinely Swift-specific construct. It is rarely used and not very Swift-like.

More Swift-typical construction is already available with for-in statements and stride. Removing for loops would simplify the language and starve the most common use-points for -- and ++, which are already due to be eliminated from the language.

The value of this construct is limited and I believe its removal should be seriously considered.

Yesterday, I was on family time. Today, I am grabbing a little Swift-break to respond to the discussion I otherwise missed on-list. Please forgive the lateness of this response.

Lack of Language Necessity

Clemens writes:
> We’ve developed a number of Swift apps for various clients over the past year and have not needed C style for loops either.


But I think this is a very weak reason for *removing* any portion like C-loops and increment or decrement operators of a programming language. How about the developers and the productive code which used C-style loops already?

I understand the reasons why you, Erica and the others don’t like C-style loops. But changing the philosophy of a productive programming language is a very bad idea.

I don't think the C-style loop was ever really part of the language philosophy. It feels like a vestigial feature that no one ever got around to cleaning up. Its entire functionality is easily replaced by other, existing Swift constructs. While Swift design deliberately held onto C-like features for familiarity, I see no special benefit to retaining the for-loop.

In contrast, consider fallthrough. There was a discussion on Friday about fallthrough, and it quickly became apparent that losing this language feature would have a significant impact on a few key algorithms. Several list participant were able to pop forward and say "without this feature, I would not be able to do X, Y or Z". I see no parallel case to be made for for-loops.

Lowered Readability and Maintainability

I have aesthetic reasons for disliking the for-loop. The C-style loop is harder to read especially for those not coming from C-style languages, easier to mess up at edge conditions, and is commonly used for side-effects which, in a language focused on safety, is not a feature to be encouraged. 

For example, one side effect that was mentioned on-list yesterday was the incrementor, which is guaranteed in C-style to execute late:
Roland King writes:

for var floatingThing = start ; floatingThing <= end ; floatingThing += delta
{
	// more than a few lines of code with early escape continues
}

shows intent of the loop very clearly, start, condition and increment all together at the top, and however you loop you always execute the increment part of the statement. Convert that to a while(), if you have a continue in the body, you have a good chance of not incrementing at all, or duplicating the increment code before every continue. So you can’t always nicely turn for into while. I second the point below about the loop variable being local to the for as well, I also like that. 

Late incrementor management is a feature that can be mimicked with defer, as pointed out by several other list members.

Another complaint regarded a loss of succinctness. Per Melin wrote: 

Another example from the same LinkedList class. It finds the right place to insert a new node:

  for next = head; next != nil && index > 0; prev = next, next = next!.next, --index { }

Extreme? Probably, but I like it better than the same thing done in five lines of while loop.

I'd argue that readability and maintainability are core API goals. Clarity is always to be preferred to brevity. Let me point you to Justin Etheredge's essay "Don't be clever" (http://www.codethinked.com/dont-be-clever). 

So next time you go to write a super clever line of code, think to yourself "Will the benefits of this super cleverness be outweighed by the future issues in maintaining and understanding the code?" And if there is anyhesitation at all, then you better not be clever, because 3 months from now you will come across that code and say "What the hell was I thinking?" Then you’ll end up rewriting it anyway.

Incrementor Challenges

Another complaint involved forgetting or misplacing the incrementor when transforming items to while loops. There are two cases being glommed together here, and I'd like to separate them.

First, there's a collection case, in which the collection provides its own indices. In such implementations, there's simply no need to manually declare and manage an index. You can use for-in.

Second, there's what I'm going to call the bitmap case, where an index may refer to geometrically-related indices, which happens often in image processing. (I'm going to repress any "Just use Accelerate" rant. This is a significant area of programming).  Here's some pseudocode that demonstrates how this might look in a for-loop-less Swift implementation:

    for row in 0..<height {
        for column in 0..<width {
            var sum: UInt = 0
            for rowOffset in -1...1 {
                for columnOffset in -1...1 {
                    let index = pixelOffsetAt(row + rowOffset, column + columnOffset) + redChannelOffset
                    sum += pixels[index]
                }
            }
            let currentRedPixelBlurredAverage = sum / 9
            // blah blah other stuff
        }
    }

Again, I don't see anything that would limit relative indexing  with this proposal.

The Skip Case

Matthijs Hollemans writes, 

Another benefit of a C-style for loop is that it simply ignores the loop when n <= i, as in the following example,

  for var i = 100; i < n; ++i { ...

while the Swifty version gives an error because it cannot create a range where the end is smaller than the start: 

  for i in 100..<n { ...

Of course, you can add an if-statement to catch this but in the C-style loop this is implicit. Hence, it is more expressive.

I may be in the minority but I rather like that this becomes an error. The "skip" behavior reads to me as an unintended side-effect rather than a purposeful design goal. I prefer a philosophy that minimizes such possibilities in a safe modern language.

Issues of Type 

Roland King writes,

I must be the only person who still likes C-style for loops on occasion. eg a loop with something floating point 

I counter that stride addresses this for all conforming strideable types, not just floating point values. 

Training Costs and Migration Benefits

The main argument against losing the feature seems to be the higher training costs for C-style coders and the higher porting costs for existing C-code.  I'd argue that the training costs to a new language are significant and whether there's a C-style for loop will not materially change those overall costs, especially for those moving from Objective-C where the for-in loop is common. Second, porting to Swift should be motivated by an enhancement of safety and maintainability. Swift already supports external calls to C routines. If you want to keep your code in C, there's nothing stopping you from doing so. (Or to put it in American Politician-speak, "If you like your C-code, you can keep your C-code")

More Direct Swift Replacements

After all this discussion, let me end with a suggestion proposed by Joe Groff for anyone who would still miss the for-loop by approximating the same control flow in native Swift:


func cStyleFor(@autoclosure init initializer: () -> (), @autoclosure test: () -> Bool, @autoclosure inc: () -> (), body: () throws -> ()) rethrows {
  // left as an exercise
}

var i = 0
cStyleFor(init: i = 0, test: i < 10, inc: ++i) {
  print(i)
}

-Joe

The only feature this does not include, as pointed out by list members, is a co-declared variable binding. In a C-style for-loop, the "i" is bound as part of the declaration. In this implementation, it requires a separate declaration line and remains in the scope for its lifetime.

Wrap-up

I hope I have addressed the concerns brought up on-list. If I missed one of yours please feel free to reply. The voting and review period will be between 7 Dec and 10 Dec. Here is the master schedule: https://github.com/apple/swift-evolution/blob/master/schedule.md

Best regards,

-- Erica



> On Dec 6, 2015, at 7:47 AM, J. Cheyo Jimenez via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> https://github.com/apple/swift/commit/0d001480a94f975355cd458973d5c51cef55bff1 <https://github.com/apple/swift/commit/0d001480a94f975355cd458973d5c51cef55bff1>
> 
> On Sunday, December 6, 2015, Clemens Wagner via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> > We’ve developed a number of Swift apps for various clients over the past year and have not needed C style for loops either.
> 
> But I think this is a very weak reason for *removing* any portion like C-loops and increment or decrement operators of a programming language. How about the developers and the productive code which used C-style loops already?
> 
> I understand the reasons why you, Erica and the others don’t like C-style loops. But changing the philosophy of a productive programming language is a very bad idea.
> 
> Cheers
> Clemens
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <javascript:;>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>  _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151206/1b756a86/attachment.html>


More information about the swift-evolution mailing list