A nifty language feature I've built into languages I've created in the past is "welse" (while...else). The syntax is basically:
while expr
{
*while body*
}
welse
{
*code to execute when expr is false*
}
Note that if the while loop exits for any reason other than the expression being false (such as a break, an exception, or a return), the welse code does not execute.
If a break exits the loop, any special code that might be needed can be placed immediately before the break. However (other than by some kludgery), there is no way to execute special code when the loop terminates normally (that is, when expr is false). welse would fix that problem.
As I've said, I've actually implemented this and it works nicely (and is quite useful on occasion). I think it would be a nice feature in Swift, too.
If expr is costly or impure, you don't want to run it again. It becomes more compelling with for loops, where you don't even have access to the expression that caused the loop to terminate. Consider this python:
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n//x)
break
else:
# loop fell through without finding a factor
print(n, 'is a prime number')
Now you can simulate this in Swift with a labelled continue:
outer: for n in 2..<10 {
for x in 2..<n {
if n.isMultiple(of: x) {
print(n, "equals", x, "*", n/x)
continue outer
}
}
print(n," is prime")
}
Both are pretty obscure cases tbh.
You can also blend into this consideration of making loops expressions:
for n in 2..<10 {
let isPrime =
for x in 2..<n {
if n.isMultiple(of: x) {
print(n, "equals", x, "*", n/x)
break false
}
} else {
print(n," is prime")
// see some of the longest threads on this forum for more
// discussion of whether a then keyword is needed here
then true
}
print(n, isPrime ? "is" : "is not", "prime")
}
Note that if the while loop exits for any reason other than the expression being false (such as a break, an exception, or a return), the welse code does not execute.
Your interpretation appears to differ from Python’s semantics. In Python:
The else keyword in a for loop specifies a block of code to be executed when the loop is finished. [...] The else block will NOT be executed if the loop is stopped by a break statement.
Examining different loop constructs, while-else may be useful in some scenarios, and possibly for-else as well. However, does repeat-else make sense? Under your definition, it wouldn’t apply, whereas Python’s interpretation might allow it — if Python had a do-while equivalent.
Could you explain the reasoning behind your approach and why it differs from Python’s?
i = 0
while i < 2:
print(i)
i += 1
else: print("i is 2 now.")
while i < 2: print("This never occurs.")
else: print("This does though.")
I don't think so. else on while is for executing the same code when the loop runs zero times as when in runs some non-zero times. repeat doesn't have the ability to run zero times, so all of the code that might go in else can just go after the repeat-while.
(Python doesn't even have do/repeat.)
i = 0
while True:
print(i)
i += 1
if i > 2: break
else: print("There's no reason for this clause to even compile.")
I wonder how often is this feature going to be needed, to warrant the associated language complexity increase? Is this more often than, say, using the now unsupported "var" parameters?
Not quite! Let’s take a closer look at the example from the article I linked:
for x in range(6):
print(x)
else:
print("Finally finished!")
In this case, as long as there’s no early exit, the else block runs once the loop completes. However, in OP’s example, either the loop body executes or the else block executes — but not both.
This kind of subtleness draws me away from wanting this feature... I found myself more often wanting a solution to, say, this (pseudo code):
var i = 0
`any kind of loop here` (for, while, repeat) {
i += 1
...
// use `i` somehow.
}
// i is not needed here. But I had to declare it outside!
// so now it is visible here!
In other words.. there are more pressing and/or inconvenient features in the language than the proposed one.
I think you just misinterpreted their post. (I do kind of agree with tera that this ends up feeling very subtle though; it’s rare enough to encounter this in Python that I never developed an intuition for this.)
A nice, but probably unintentional, side effect of the fact that repeat/while was originally spelled do/while before it was decided that syntax should be repurposed for its current use is that all of the control flow keywords work inside of a labeled do block.
This means pedagogically, if someone wanted to explain how loops work then he could desugar it into a do block.
// while(condition) loop
`while`: do {
guard condition else { break `while` }
// body
continue `while`
}
// for element in sequence loop
do {
var iterator = sequence.makeIterator()
`for`: do {
guard let element = iterator.next() else { break `for` }
// body
continue `for`
}
}
// repeat while(condition) loop
`repeat`: do {
// body
guard condition else { break `repeat` }
continue `repeat`
}
// repeat loop
`indefinite`: do {
// body
continue `indefinite`
}
personally, I always wanted a for-else construct, where the else block executes if and only if the for branch is never executed for any value in the sequence (thus, including empty sequences)
thus:
for x in [Int]() {
print(x) // this is NOT executed
} else {
print("not found") // this is executed
}
for x in 1...3 where x > 3 {
print(x) // this is NOT executed
} else {
print("not found") // this is executed
}
for x in 1...3 {
print(x) // this is executed
if x < 4 {
continue
}
} else {
print("not found") // this is NOT executed
}
for x in 1...3 {
print(x) // this is executed
if x == 2 {
break
}
} else {
print("not found") // this is NOT executed
}