While loop - conditional check in the middle


#1

This is something that has always occurred to me as missing from every language.
I don’t really know how or if it should be implemented, I just want to ask for your opinion.

There are 2 variants of a while loop -
while (cond) {} and do {} while (cond)
The prior will evaluate the condition and break if it does not evaluate to true BEFORE execution of the code block.
The latter will evaluate the condition and break if it does not evaluate to true AFTER execution of the code block.

What I always run into is the option to do this in the middle of the code block, without inverting the condition and then having a break.

Usually, the case looks something like this:

while (true) {
    val a = queue.poll()
    if (a == null) break
    // do something with a
}

It would be interesting to have a variant that changes the syntax of this.

loop {
    val a = queue.poll()
    condition (a != null)
    // do something with a
}

Here, the function condition will take a given boolean and break if it is false, in the middle of the loop its code.
It is possible to achieve this using inline functions and receivers but that would impact performance,
as you either need to make it use coroutines or throw an exception to stop in the middle of the block.

This would, in my opinion, be much cleaner than:

var item = queue.poll()
while (item != null) {
    // do something with a
    
    item = queue.poll()
}

Or:

var item: T? = null
while (queue.poll().also { item = it } != null) {
    // do something with a
    // oh wait the compiler doesn't understand that `item` cannot be `null`
}

Now, given this idea, you might ask: Is this feature necessary? Does this feature not bloat the language which already has a very rich feature set? Yes it does, and no it is obviously not necessary, but it does bring some cleaner code to the table which is something to consider. Any suggestions for improving the syntax or making it more backwards compatible so that it doesn’t look alien compared to existing loops are of course welcome.


#2

While I think the idea is interesting I don’t think there is any reason to add it to kotlin. In my experience this would be used only rarely and it would as you pointed out bloat the language.
Also I don’t see the problem with if(condition) break and I don’t think there is a good reason for adding a special syntax here.


#3

Note that break is an expression inside a loop, so in your case you can use it like:

while (true) {
    val a = queue.poll() ?: break
    // do something with non-nullable a
}

#4

That kind of just ignores the idea, but I agree that it’s not something exactly high priority to even be thinking about. I have to say the ability to use control flow keywords such as break in an expression is a very nice feature of Kotlin, and it certainly helps with this problem! Thanks.


#5

I don’t understand why every new language keeps copying existing loop constructs. I really hate break and continue because the loop condition is then spread out over multiple locations. I should be able to understand when the loop stops by reading the expression in for, while or until.

I think code can be a lot clearer if you could compose loops using multiple conditions. Here is an example for an eternal loop (which does not really exist IMHO):

loop {
    // Do forever
}

The solution with condition(...) @dico.karssiens proposes would be written as:

loop {
   val a = queue.poll()
} while (a != null) {
    // Do something with a
}

And loops that have 2 conditions can be written down clearly too. What if you need to print the lines in a file, but must stop after 10?

var numberOfLinesPrinted = 0
loop {
   val line = fileReader.readLine()
} while (line != null) {
    println(line)
    numberOfLinesPrinted++
} until (numberOfLinesPrinted == 10)

The example above does not have a block after until, but this is perfectly valid. And while does not have to have a following block. The loop above can be rewritten as:

var numberOfLinesPrinted = 0
loop {
   val line = fileReader.readLine()
} until (line == null) {
    println(line)
    numberOfLinesPrinted++
} while (numberOfLinesPrinted < 10)

Of course, there are 2 major problems with this syntax:

  1. The scoping of variables in the blocks of the loop. You want to be able to use a and line in following blocks.
  2. It is too late to add a new loop construct to Kotlin, especially because the keywords are already in use.

#6

Well, I love it. It sure is a matter of taste but I do the same separation with return conditions in a function:

fun f(a: Int?, b: Int?): Int {
  if (a == null) return false
  if (b == null) return false

  return a == b*2
}

I find this requires less mental effort to understand, than 1 boolean expression that uses multiple boolean operators.

You can think of more complex examples that would require complex nesting in parantheses.


#7

It still kind of just sounds like you’re talking about break. In your case you only want one break point, but there’s no reason why there only NEEDS to be one break point. Do you really find that much value in the type-safety of a compiler enforcing exactly one scope-level break per cycle? Just seems new-for-the-sake-of-new.


#8

I am not at all:

  • Suggesting that there can only be one breakpoint using this syntax
  • Suggesting change for the sake of change
  • Advocating this idea massively. I’m merely looking for a discussion to hear what other people think.

What I am doing (or meaning to do) is:

  • Pointing out that the place where you put your loop conditions is always at the very bottom or at the very top of the loop, when there are many examples of cases when you would want to have a condition somewhere in the middle, and not even one on the bottom or the top.
  • Saying that there’s no way of expressing this other than using an inverted condition along with break (when the loop conditions otherwise don’t need to be inverted).

#9

Yes, there’s no other way to express this other than the way to express it. As far as I can tell the only non-aesthetic difference between your proposal and a standard break is that it enforces the break to be once-only and scope level, and frankly I’m not seeing a pressing need for those limitations. It’s syntax creep.


#10

Again, the thing I had in mind does no such thing as limit the number of breaks in the loop to 1.
That being said, I can agree with what you say here. There are no good reasons to implement this. Syntax creep is a nice way of putting it as well.

I guess nobody else gets annoyed by this from time to time, haha.


#11

I’m not sure if this has been mentioned yet or not, but it sounds like an IDE inspection would solve this problem.

An inspection wouldn’t impose any change on the language or standard library. An inspection could be configurable, enabled/disabled, and @suppressed.

If what we really want is a way to avoid the code smell of multiple breaks in a loop, an inspection seems like a natural fit :smile: