I’m sorry to resurrect this old topic, however, the suggestions provided are awkward so I’d like to explore them. They work sometimes, but there is a very cool Kotlin language design features that do not interact with them well. In particular, its the val/var options and how “try” is an expression not a statement. So in Kotlin you can write this:
val a = try { … } catch { … }
It feels to me very common to want to return a value out of a try block!
But you cannot do so using the first suggestion, because “for” is not an expression:
val a = for(i in 0..2) { // doesn't work
try {
doSometihng()
break
}catch (e: Exception){
...
}
}
You can use “var” and then set it inside the try and catch, but if the variable is actually a “val” except for this initialization clause, you can’t take advantage of “val”'s benefits. Additionally, you either need to let the var be nullable or init it to a dummy.
What about while loops? Same problem. while loops are statements.
Ok how about wrapping it into a function? Something like this:
fun<T> retry(count: Int, fn:(Int)->T?):T
{
for(i in 0..count)
{
val tmp = fn(i)
if (tmp != null) return tmp
}
throw RetryExceeded()
}
Ok, first problem: awkwardness; we either need to create a new exception to be thrown (as shown) or return null which puts that pesky null back into the code.
Second, there’s this hidden (implied) idea “if you return null, repeat, otherwise return T” in the user code. Here’s an example use:
val data = retry(5) {
try
{
server.request(req, timeout=1000)
}
catch (e: RequestTimeout)
{
server.close()
server = connectToAnotherServer()
null // ??? weird
}
}
Note that this also hides the real exception (RequestTimeout). Perhaps what we really want to do it replace the null with:
“if (it < 5) null else throw e”, so that in the last iteration it throws the real error rather than RetryExceeded()? Awkward.
Ok, let’s propose a statement like “break” and “continue” called “again”, and as the OP suggested, allow an optional count as part of the try opener. The semantics of “try(n)”, “again” are that “again” will execute the try block up to n times (as if there was a counter variable that is incremented at the beginning of the try block), and when that’s over, throw e.
val data = try(5) {
server.request(req, timeout=1000)
}
catch (e: RequestTimeout)
{
server.close()
server = connectToAnotherServer()
again
}
Anyway, this proposal is cleaner but certainly not perfect. My intention in posting this is more to observe how useful try-as-an-expression is, and yet its use is defeated by the very common try catch design pattern where you want to make a change and retry.