Make it easy to repeat something until it succeeds, but at most n times


#1

These loops:

for (i in 0..2) {
    doSomething()
    if(somethingFailed()) {
        continue
    }
    break
}

could maybe be expressed better.

Something like this would be nice:

try (maxTries = 3) {
    doSomething()
    if(somethingFailed()) {
        retry
    }
} 

And:

try (maxTries = 3) {
    doSomething()
} catch(_: BadException) {
    retry
} catch (_: maxTriesExceeded) {
    log("big problem")
}

#2
for(i in 0..2){
	try {
		doSometihng()
		break
	}catch (e: Exception){
		...
	}
}

This can easily be done inside of the language already and I don’t think it is used often so I don’t think it is worth extending the standard library for it. Why not just create a simple utility function in your project?


#3

Alternatively you can do it as a sequence with takeN and firstOrNull. In some cases you can just use the range as a sequence/list instead of using takeN


#4

Well, if the program is very short, of course it makes no real difference. But I think that it would enhance readability in long programs.

Currently, everyone solves it in another way, I think that a unified solution would be better.

And also, all of the solutions have disadvantages: Like, if you choose Wasabi375’s solution. Some weeks later you want to add logging and log a warning in case it failed all 3 times.
Of course this is possible but it is more complex than it could be.


#5

A simple while statement will do. No jumps using break or continue, and a simple condition that clearly states when the body will be executed:

var hasSucceeded = false
var numberOfTriesRemaining = 3
while (!hasSucceeded && numberOfTriesRemaining > 0) {
   numberOfTriesRemaining--
   doSomething()
   hasSucceeded = !somethingFailed()
}

This also allows you to check the final result after the loop.


#6

I don’t think this needs to be added to the language. Retry logic could be done with functions:

fun main(args: Array<String>) {
//sampleStart
    retry(5) {
        println("Will succeed first attempt\n")
    }
    
    retry(5) {
        throw Exception("Will fail after 5 retry attempts")
    }
//sampleEnd
}

fun <T> retry(numOfRetries: Int, block: () -> T): T {
    var throwable: Throwable? = null
    (1..numOfRetries).forEach { attempt ->
        try {
            return block()
        } catch (e: Throwable) {
            throwable = e
            println("Failed attempt $attempt / $numOfRetries")
        }
    }
    throw throwable!!
}