The C-style for ;; loop simply has no place in Swift.

Recently, it has been announced by Chris Lattner himself that Swift will do away with the classical C-style for ;; loop. The suggestion was done by Erica Sadun, one of the most respected members of the iOS Development Community, and her proposal has been accepted for Swift 3.0. This for loop is used in almost every programming language, from Java to Objective-C (of course, it’s just a superset of C after all) and beyond.

It sparked this Reddit discussion about the decision. Many people support the removal of the for ;; loop and many of them seem to be worried. In this post I want to explain why I support the elimination of the C for loop, and why people shouldn’t be worried about its removal in the future.

In this post, I want to urge you to write code with as little loops as possible, and to embrace higher order functions whenever possible. At no point will I suggest you stop using loops altogether. If anything, use the other loops (for-in and repeat-while) instead of for ;;.

Everything that can be done with a loop, can be done without it.

Swift will keep some of its loops, like repeat-while and for-in, so it’s not like Swift is getting rid of the ability to loop altogether.

But even if it were, there are more, cleaner, alternatives to loops. Let’s talk a little about functional programming, which Swift seems to embrace a lot, to see why.

Functional programming is the programming paradigm that embraces immutability and the reduction of side-effects. Think about the difference between let and var. If you are reading this, you are familiar with how these keywords work. Anything you declare with let cannot be changed. Anything declared with var is fair game to be changed in the future.

In a pure functional coding style, you would avoid using var because everything is immutable. This means that there cannot be the concept of a counter variable, because they are incremented or decremented at some point, and that’s not immutability. Now of course, in the real world it is impossible to achieve full functional code, since any user interaction that happens must cause something to happen somewhere in your program. But still, I believe coding “functionally” whenever possible is going to help you write much better code, and using as few little loops as possible is going to help you do that. Getting rid of the classic C-style loop is a very good beginning to start writing functional code.

Like I said in the Reddit thread I mentioned above, I took a functional programming class in Scala in which we didn’t write a single loop, despite the fact that Scala for loops don’t technically violate the principle of immutability. Can you imagine writing a lot of code without a single loop, despite tasks that may need them? It’s entirely possible using recursion and higher-order functions.

Recursion

Recursion is an important topic, and arguably one of the hardest topics for newcomers in programming to understand. One of the reasons people are hating the removal of the C-style for ;; loop is that they say transitioning to Swift from whatever your first language is will be hard, and if you have been programming for a while, it will be hard to get used to. This is not so necessarily true because the concept of recursion is the same everywhere, so even if learning the Swift loops and higher order functions was too complicated, you could still achieve the same results using recursion. This is a big even if, because Swift’s loops are easy, and higher order functions are not too complicated to understand. Really, the only reason you would want to keep for ;; around is to make it easier than it already is to transition from any other language to Swift, and for legacy reasons. I imagine a programmer who has been writing code for the last 20 years has written many for ;; loops, but programming paradigms change, some are introduced, and it is all for the better for Software Development. We simply cannot cling to all idioms and features of old languages for too long.

I think recursion has its place. Some algorithms are better with it, and some of them are more complicated than they should be. Use recursion when it makes sense. I am not saying you should ditch loops altogether after all, and I definitely don’t want people to write over complicated algorithms with recursion when they could have been done with a Swift loop (as long as the loop in question is not a for ;;).

Higher Order Functions

Higher order functions can replace loops for around 90% of tasks that require them, I’d say. The Wikipedia page on Higher-Order Functions defines a higher order function as:

In mathematics and computer science, a higher-order function (also functional, functional form or functor; not to be confused with the functor concept in category theory) is a function that does at least one of the following:

  • takes one or more functions as arguments,
  • returns a function as its result.[disputed – discuss]

This is the definition copied and pasted from Wikipedia as of December 18, 2015 at 2:35 PM – Please note that the second bullet point is marked as disputed.

In short, a higher order function can take one or more functions (if you really are an old school programmer, think of it as a function taking a pointer to a function in C++. If you have worked with C# before, think of lambdas. And, if you are writing in Java 8 and above, think of anonymous functions) and then execute them. Note that the Wikipedia article says that a higher order function returns a function and it is marked as disputed. And rightly so – I’d probably say that a higher order function takes one or more functions as parameters and it might return another function – That is, I wouldn’t say returning a function is required for it to be considered a higher order function. Swift includes many higher order functions for collections that help you reduce the usage of loops.

If you are new to programming, this discussion may be hurting your head a little bit. The good news is I have code examples ready to illustrate how you can reduce the use of loops altogether.

For this example, I will adapt some old code of mine to loops, and then I will show you how to reduce it to using HOFs. This code comes from my app Mignori, and it’s purpose is to run through a JSON response from a web service and to map each JSON object to a class manually with the help of SwiftyJSON. I am not using an object mapper for this as I consider it to be overkill for my specific purpose, but I digress.

You could start with the “naïve” approach. Suppose you just talked to a web server and got a lot of JSON back and you want to map it to your own class. You could do this (I need to state I never used this for loop in the app, but I adapted it from the second snippet):

let json = JSON(data: data)
var createdPosts = [Post]()

if let posts = json.array {			
	for var i = 0; i < posts.count; i++ {
		let post = try Post(json: posts[i], server: self.server)
		createdPosts += [post]
	}
}

Simple enough right? We got the results as a JSON array, and we are going through each element to create a Post with it. The details of a Post object are not important in this article.

If you have been programming for a while, particularly in Swift, you may notice that we can do the same thing, in a cleaner manner, using a for-in loop.

let json = JSON(data: data)
var createdPosts = [Post]()
            
if let posts = json.array {
	for post in posts {
		let post = try Post(json: post, server: self.server)
		createdPosts += [post]
	}
}

This will iterate through each element and then create a Post object with it, without needing to use the array index.

But this can be even better, by using the map higher order function. This function will iterate through each element of the array, do something with it, and return it. In this case, we are converting a raw JSON object to a Post object. That is, we are mapping JSON to Post.

let json = JSON(data: data)
let createdPosts = try json.array?.map({ (jsonPost) -> Post in
	return try Post(json: jsonPost, server: self.server)
})

The map higher order function takes a function. The function you pass as a parameter is the operation that will be done to each object of the JSON array. The map function itself will return a new array – In this case, a new array of Post objects.

This is much, much better and more functional than the approaches described above. The best part is you can still simplify it further without hurting readability:

let json = JSON(data: data)       
let createdPosts = try json.array?.map { return try Post(json: $0, server: self.server)}

Terrific! Something that took us over 5 lines of code before, now takes us two! The $0 is a placehorder for the value the function is giving you – In this case, the JSON Post. If the function had two parameters, you could use $0 and $1 – and so on.

Important Higher Order Functions

If I managed to show you how powerful higher order functions are, then there’s a few of them you should know about. I am not going to explain to you how they work, and I will leave them as an exercise for yourself. Keep in mind you can apply these functions to any collection that conforms to the CollectionType protocol, and as such you can create your own collection types that support these HOFs.

  • map: Map is the function we just saw, which allows you to to operate over the elements of a collection and then return a new array with the results.
  • filter: The filter function will take a function that returns a boolean. You will decide what objects should be part of the new array and which ones won’t. For example, if you have an array of numbers that go from 1 to 200 sequentially, you can filter the array to return a new one that contains only even numbers.
  • reduce: The reduce function will collapse a collection into something else. For example, suppose you have an array of numbers, and you wanted to add them up and return that result. You could achieve this using the reduce function..

There’s many other higher order functions to explore, like flatMap, but they are not as widely used so if you want to know more about them, check the Swift documentation.

When to use Loops

To end this discussion, let’s talk about when you may actually want to use loops. Like I said before, my intention is not to destroy the usage of loops altogether, but rather to decrease their usage as modern programming languages have better alternatives for many cases.

First, don’t use C-style for ;; loops. They are not needed, not at all, and they are going to be removed for Swift 3.0 anyway.

Second, in the case you need to iterate through a collection and you need the index in each operation (basically the only valid usage for for ;; loops I can think of), then you can use the enumerate() method:

for (index, value) in shoppingList.enumerate() {
    print("Item \(index + 1): \(value)")
}

(Code taken from the official Swift guide)

Third, even non-enumerated for-in loops have their uses. I use them to print quick debugging info:

for tag in tagsArray {
	print("tag is \(tag)")
}

Finally, the repeat-while and while loops. These loops can have their use cases, but DO NOT use them to iterate through collections. There’s simply no need to do that. To be honest I haven’t needed to write one of these loops in a long time. I would say their uses are limited, but you would use them for things like this:

while !coffee.empty {
	coffee.drink()
}

Use these loops when you need to do something as long as a condition is true. I repeat, do NOT use them for iterating through collections. Because while you can do that, semantically it doesn’t make any sense. You have all the alternatives for that above.

Conclusion

I welcome the removal of the C-style for ;; loop, because modern languages have better and more robust alternatives to do what you would do with them. We cannot stick to legacy conventions forever, and removing the for ;; loop is a great first step to get rid of them. This may not apply to all languages, but it definitely applies to Swift.

I encourage every developer to adopt the usage of functional paradigm philosophies when it makes sense, and to decrease the usage of loops throughout their code whenever possible.

Positive SSL