Kotlin尾递归函数改造:用标准库Result替换Arrow Try/Either遇类型问题
用Kotlin标准库Result重构尾递归重试函数的返回类型问题解决
核心问题在于Kotlin标准库的Result<T>是包装类型,直接返回会得到Result<T>,而原函数需要返回T类型。要实现原功能,需要在成功时解包Result,失败时根据超时逻辑决定重试或抛出异常,同时保留尾递归特性。
修正后的实现代码
import kotlin.system.currentTimeMillis // 同步版本(基于Thread.sleep) tailrec fun <T> eventuallyAux( fn: () -> T, startTime: Long, timeout: Long, delayMs: Long ): T { val executionResult = runCatching { fn() } return when { executionResult.isSuccess -> executionResult.getOrThrow() // 成功时解包返回T类型值 currentTimeMillis() - startTime > timeout -> { // 超时后抛出捕获的异常,与原函数行为一致 throw executionResult.exceptionOrNull()!! } else -> { Thread.sleep(delayMs) // 尾递归调用重试 eventuallyAux(fn, startTime, timeout, delayMs) } } } // 协程环境下的挂起函数版本(保持尾递归) import kotlinx.coroutines.delay tailrec suspend fun <T> eventuallyAuxSuspend( fn: suspend () -> T, startTime: Long, timeout: Long, delayMs: Long ): T { val executionResult = runCatching { fn() } return when { executionResult.isSuccess -> executionResult.getOrThrow() currentTimeMillis() - startTime > timeout -> { throw executionResult.exceptionOrNull()!! } else -> { delay(delayMs) eventuallyAuxSuspend(fn, startTime, timeout, delayMs) } } }
关键逻辑说明
- 替代Arrow的Try:用
runCatching { fn() }将函数执行结果包装为Result<T>,实现原Arrow库Try的成功/失败捕获功能。 - 成功解包:通过
getOrThrow()将Result<T>转换为T——因为此时isSuccess为true,该调用不会抛出异常,直接返回目标类型值。 - 超时处理:当超过设定超时时间时,直接抛出
Result中捕获的异常,保持原函数"要么返回T,要么抛出异常"的行为。 - 重试逻辑:未超时的失败场景下,等待指定延迟后尾递归调用自身,保留原有的重试逻辑和尾递归优化特性。
注意事项
exceptionOrNull()在isSuccess为false时必然返回非空值,因此使用!!是安全的;若想更严谨,可改用requireNotNull(executionResult.exceptionOrNull()) { "Failed Result has no exception" }。- 若原函数允许超时后返回默认值而非抛出异常,可将超时分支改为
return defaultValue,但需保证返回值类型为T。
内容的提问来源于stack exchange,提问作者Jonesh Sharma
相关产品推荐
相关产品推荐

