Can we please get a while with a post-loop test?

In writing my own code lately, I continue to get annoyed with needing to write out wonky boilerplate just to get a post-body while test. Even worse, I find myself eschewing the increment part of Zig’s while construct (and presumably giving up on nice compiler optimizations) when using “while (true)” because it moves the test and increment away from one another visually.

So, I decided to check std to see if I’m just the only person suffering:

√ std % rg "while \(" | grep while | grep true | wc
    458    2122   19614
√ std % rg "while \(" | grep while | grep -v true | wc
   1524   13297   99130

Post-body while loop checks seems to be 20% of all while loops. That seems like it passes the threshold to start thinking about an actual language construct.

This doesn’t have to be a whole new grammar construct like do/while. It could be as simple as adding using a new keyword like whilepost that uses all the same while machinery, but only changes the position of the conditional test in the generated code. (Yes, I realize that the coder view of the complexity is easy while hiding complexity in the compiler side of the equation).

Thanks.

Not every while (true) loop is a while (true) { do_stuff; if (cond) break; } loop. If the break is done by a return statement or the break condition is in something like a switch, orelse, catch or nested if, a while loop might still be less complex than a do-while like loop. Undoubtedly some loops might benefit, but not 20% of all loops.

I miss the very clear and handy Delphi one: repeat until.

2 Likes

This is by no means against you, but repeat ... until is the devils loop. It just inverts all the other common loop idioms.

Normally in a while or for loop you loop while some predicate holds. repeat until loops while it doesn’t hold.

I still hope we get a do while in zig. It isn’t needed often but when it matches the desired effect it’s much clearer than any workaround with break in various places.

2 Likes

IMHO repeat … until is much clearer than do … while, in particular because I also have seen while loops without a body in some cases. If you read untilthen you can be sure that this ends a loop with a body.

But in the end it’s just a matter of taste.

3 Likes

I could agree with this in a vacuum, but assuming that we don’t ignore decades of context, history, and convention in the many other languages that use it, do...while is clearer by virtue alone that it is the far more familiar/common construct.

While I personally appreciate the inclusion of while in Zig and use it often, I have no opposition with languages like Go that just use for for every loop, so take my perspective with a grain of salt.

To give a different data point:
In my game there are 551 while loops, 31 of them are while (true) but only 4 of them actually would be convertible to do while. And one of them would be better implemented as a helper function anyways.

The main reason for this is that often the condition depends on some loop-local variable, or a temporary value (e.g. orelse break), or the loop just wants to return a value when the loop ends (either from a small helper function or a block).
I’m sure when you look through std you’ll find that most while (true) loops you found suffer from similar problems.

2 Likes

Honestly.I think they’re both clear just fine because we can all read English.

On reconsideration: I checked my program. 53 while loops and 13 while (true) loops.
Mostly these last ones are very short and a break is (I did not use Delphi for ages) in most cases shorter and easier on the eyes for me now.
I vaguely remember sometimes using 'repeat … until false` with a break hahaha :slight_smile:

For me, do…while’s biggest advantage is that “until” makes me squint and wonder if it’s inclusive of the outer bound or not.

var nine_to_five = .init;

repeat {
  job();
  nine_to_five.increment();
} until (nine_to_five.is_five())
2 Likes

Have you considered the following form?

while (true) : (if (!shouldDoStuff()) break) {
    doStuff();
}
11 Likes

I’d vote for removing while loops with continue expressions from the language.

I actually had to read the docs to understand it, plus, it’s confusing for me as a Python programmer.

It reminds me of the infamous C for loop syntax.

Just an if with a break inside the loop is the most straightforward solution for me.

PL/SQL has an exit when which is even more readable, but I think that’s incompatible with loops as expressions.

1 Like

?

This is in no way different from do … while except the negation of the condition.

Maybe it’s just me then, but “count until nine” to me is ambiguous in a way that “keep counting while it’s not nine” isn’t.

3 Likes

How often does it happen that you’d a perform a post-loop test that doesn’t depend on variables that logically should only exist within the scope of the loop?

Mostly stuff the new std.Io covers, I think? Things that are outside of one’s control: clocks, accessing unreliable hardware resources, async, etc.

while (!shouldDoStuff()) {
    doStuff();
}

easier

1 Like

I believe the version by @castholm runs at least once, just like repeat … until would.

3 Likes

Experienced developers like @chrboesch misinterpreting this is another indicator that the continue expression should be considered for removal.

An if … break inside the loop, at the place where you need it, is two keywords more boilerplate, but on the plus side, the semantic is absolutely clear.

The continue expression is written in a totally different place (before the body) than executed (after the body); this is confusing.

2 Likes

Do you also find defer confusing for the same reason? Continue expressions are very useful, including multistatement ones, and the Zig compiler has many good examples. Also note it’s not executed after the body per se, it’s more useful to think of it as executing on continue (explicit or not)

5 Likes