While... else in Kotlin

Hi,

there’s one feature in Python which I would really like to see in Kotlin: the while-else loop. The basic idea is: you can attach an else block to all sorts of loops. This block is executed if the loop did not execute at least once. For exanple:

var running: Boolean = shouldRun()
while(running){
    println("run")
    running = shouldContinueRunning()
} else {
    printlin("never been running")
}

This construction allows for simpler control flow in cases where a default action instead of loop execution is required. Currently, a workaround involving a mutable boolean (or int) variable is required to achieve this behaviour.

2 Likes

Your description of the semantics of a while-else loop in Python is off the mark.

According to the Python documentation the else clause executes when the condition is false whether the loop body was executed zero or more times. The only way to avoid execution of an else clause is to terminate the loop through a break statement.

Okay, I didn’t know that. A coworker briefly showed it to me, I’m not really a python user. Regardless, the “do something if the loop never executed” semantics is very appealing to me :slight_smile:

I’m strongly against this syntax. Always hated it in Python and don’t think that else in such place is readable in any way. I always forget in Python when else closure is going to be executed.

If there was need of introduction such construction which executes only when loop is broken not by break statement then I’d prefer smth like this:

while (condition) {
    code
} notbreak {
    // executed only when `while` is broken not by `break` statement 
}

But I don’t think that such need exists

1 Like

Small side note: if you want you can emulate Python’s while-else behavior such way:

class BreakCalled : Exception()
inline class WhileLoopSyntheticResult(val endedByBreak: Boolean)

object WhileLoopBodyContext {
    val Break: Nothing
        get() = throw BreakCalled()
}

inline fun While(condition: () -> Boolean, body: WhileLoopBodyContext.() -> Unit): WhileLoopSyntheticResult {
    var endedByBreak = false
    while(condition()) {
        try {
            WhileLoopBodyContext.body()
        } catch (ex: BreakCalled) {
            endedByBreak = true
            break
        }
    }
    return WhileLoopSyntheticResult(endedByBreak)
}

inline infix fun WhileLoopSyntheticResult.Else(elseBody: () -> Unit) {
    if (!this.endedByBreak) {
        elseBody()
    }
}

fun main() {
    var i = 0
    While({ i < 5 }) {
        if (i == 3) {
            Break
        }
        println(i)
        i++
    } Else {
        println("Else not executed!")
    }
    i = 0
    While({ i < 5 }) {
        println(i)
        i++
    } Else {
        println("Bam! Else executed!")
    }
}

Of course it has performance overhead. Especially exception thow

2 Likes