Absense of normal for loop

A little of grumbling again.

I cannot figure out what was the reason for not having normal ‘for’ loop in the language. All those projections may be ‘nice to have from time to time’ sort of syntactic sugar, but in general I believe that was a stupid idea to get rid of normal for loop.

Can anybody please exaplain why all these weird constructs like ‘step’ , …< ‘downto’ ‘until’ etc were deemed by Kotlin authors better than the old good, simple, strightforward, crystal clear for loop?

for (i = 0; i < length; i++)
for (i = length -1; i >= 0; i–)

?

1 Like

I can’t say what was the reasons exactly as I’m not a Kotlin author, but I believe pretty much the same reason we had a stupid idea of removing the old good, simple and straightforward goto. It is a lower-level construct which have cleaner, high-level alternatives. Also, for doesn’t provide too much benefits over the while.

See: Any reason to not keep the good old for loop format?

7 Likes

First of all, if you write idiomatic Kotlin, you write way less for loops to begin with, e.g. there are map and flatMap and foreach and their indexed variants. Other than that, the classical loop format isn’t that “simple” and “straightforward”, it has a lot of moving parts, a changing index variable, which may be accidentally changed in the loop body. The “weird constructs” are very easy to understand: They define ranges, which have a predefined way to cycle over them - no margin for error there, once you got the right range.

All in all, the classic format is not “bad” as such, but it operates on a lower level: You are dealing with every moving part of the loop mechanism: “Make this variable, do something with it, change it somehow, repeat as long as this condition holds”. Kotlin code tends to operate on a higher conceptual level: “Transform all elements of the list using this operation”.

This focus on what to do, instead of how makes the code more readable and less error-prone. I know it needs time to get used to, but at the end, it feels like a promotion where you can delegate a lot of boring detail work to that compiler guy, and you have your head free for the bigger picture.

12 Likes

Tbh I like the syntax for simple loops:

    val list = listOf<Int>()
    for (i in 0..<list.size) {
        // do stuff
    }

I am in leetcode grind phase now, and the syntax 0..<bound helps me visualize/map to the math operators [0,k) or 0 <= ... < k. In environments (LC) with no autocomplete I find it quite helpful.

On the other hand, the c-like loops are also pretty readable, but a bit wordy compared to Kotlin’s simple loop.

Your example is actually the opposite of the “Kotlin way” and going a step back in the direction of classic loops. If we need to iterate over a list, we should do simply: for (v in list) or list.forEach { v -> }. If we need the index, then: for ((i, v) in list.withIndex()).

3 Likes

Other available options for loops:

val list = listOf(1, 2, 3)

// iterating through indexes manually
for (index in 0..list.lastIndex) {
	println(index)
}

// a more convenient alternative
for (index in list.indices) {
	println(index)
}

// iterating a list in reversed order (no-copying)
for (element in list.asReversed()) {
	println(element)
}
1 Like

It’s not “simple, strightforward, crystal clear”, it’s just your old habits from C language. (Or JavaScript maybe)

7 Likes

I agree

I read that the main raison is the syntax that doesn’t fit with the language paradigm.

Kotlin and Swift share the same language paradigm (secure by construction). In Swift they had implemented this syntax in early versionb (previously to 2). But they removed ‘variable++’ syntax as it is off of the language paradigm… so it became evident to have to remove the traditional C for-loop syntax. So Swift 3 lost it.

Looking at usage we are far more iterating with a step of 1 than other, and far more iterating before the count/size of an array than other. So we wrote far more ‘i < size; i++’ that other. And because we are iterating a lot for array iterations, the for-in-array became the more used construction.

As says @GeorgePap-719 for the main need of iterating a defined numeric sequence, the syntax with range (half-open) is very more simple AND READABLE.

0..<size, 1..<size, n..<m 

The new version will also adopte what that existed in swift the range including the end (closed-range).

0..size, 1..(size+1)

The reverse side if far more explicit than C like version

size downTo 1

And the stepping is very explicit also

1..<size step 2

Note also that the original C syntax included the possibility of:

1- implementation in line because so in (int i = 0 ; i < n ; i++) the var ‘i’ is not a constant varying under the hood but a true mutable one, so you can change it during the loop that is bug generating.

2- as it was implementation it supported the syntax ‘variable++’ but also "variable–', ‘++variable’ and ‘–variable’ that was a source of readability complexity, and needed to implement the ‘++variable’ & ‘–variable’ in the language to be predictive with the C syntax, that was a risked syntax.

(int i = 0 ; i == n ; ++i)  <<  'i' is iterated before being considered 
                                so what happens really here?
(int i = 0 ; i == n ; i++) << is it different from the previous syntax?

3- multi-member, multi test, multi implementation syntax that could lead to very complexe syntax

(int i = 0, int j = -1, z = i ; j < i, i < n, z > j; i++, ++j)

even thought we wrote that a lot for sorts, matrix journeys we have to recognize that we spend an enough important time to consider (and debug) what was the intent here and figure out the variation.

So on my side when it turns for me to write C++, I avoid this kind of loop and prefer to keep simple if I must use it.

4 Likes

Although the examples are for Java not Kotlin this talk gives good rationale why “imperative” style is inferior to functional programming, which is why they did not include it

There is no case where the C style for is superior

1 Like

I would reverse the argument, what is the argument FOR having for the 50-year old C-style for syntax in the language? It is usually more confusing, harder to read, longer, and more error-prone and there is no case I can come up with where it would be preferable to the plethora of alternatives that are provided in Kotlin. The fact that other languages have it is not a winning argument since as someone else pointed out with goto.

Please provide any example where this style of for is superior to anything that Kotlin offers. Your examples are not good ones. In all likelihood they are probably better replaced with forEach, but you don’t provide context for where length comes from. Note that you didn’t actually provide a type on i so your examples are not even stand-alone. These are error-prone because it is very easy to get errors by:

  • using <= instead of <
  • starting at 1 instead of 0
  • forgetting that you need the -1 in the second case
  • forgetting the = in >=

to name just a few.

(0..<length).repeat { .. }
(0..<length).reversed().forEach { .. }

is more concise and less error-prone, though there are better alternatives if simply iterating a collection.

2 Likes