Jason5lee
01/06/2022, 1:54 PMthrow is unreachable. But after I removed it there is an compile error. Is it a known issue?Kirill Grouchnikov
01/06/2022, 1:56 PMthrow as unreachable because you have an infinite loop. The second piece says that since you never return and never throw, this function can not return the promised IntJason5lee
01/06/2022, 1:57 PMInt either.Kirill Grouchnikov
01/06/2022, 1:58 PMthrow that is considered to be a "valid" escape from such a function. Except that the throw can't be reached either.Jason5lee
01/06/2022, 1:58 PMrun is removed.ephemient
01/06/2022, 2:12 PMthrow expression has type Nothing, loop has Unit type. although arguably an infinite loop should also have type Nothing, Kotlin compiler can't infer that now. https://youtrack.jetbrains.com/issue/KT-25023Jason5lee
01/06/2022, 2:49 PMYoussef Shoaib [MOD]
01/06/2022, 4:22 PMephemient
01/11/2022, 2:23 AMinline fun loop(block: () -> Unit): Nothing {
while (true) block()
}
but the Rust version also allows for break value, which I don't see an elegant way to implement in Kotlinephemient
01/11/2022, 2:46 AMinterface LoopScope<in T> {
fun emit(value: T): Nothing
}
class Return(val value: Any?) : Throwable("", null, false, false)
inline fun <T> loop(@BuilderInference block: LoopScope<T>.() -> Unit): T {
val loopScope = object : LoopScope<T> {
override fun emit(value: T): Nothing {
throw Return(value)
}
}
try {
while (true) loopScope.block()
} catch (e: Return) {
@Suppress("UNCHECKED_CAST")
return e.value as T
}
}
or
suspend fun <T> loop(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): T = flow {
while (true) block()
}.first()
which can both be used like
assert(loop { if (Random.nextInt(10) == 0) emit(true) })
but both implementations have pretty annoying downsidesYoussef Shoaib [MOD]
01/20/2022, 3:58 PMinline fun loop(block: () -> Unit): Nothing {
while (true) block()
}
fun main() {
val isZero = run {
loop {
if (Random.nextInt(10) == 0) return@run true
}
}
}
In Kotlin, whenever you find the notion of "breaking out" of some scope, one of the first things you can try is just to nest that scope inside a run.
Alternatively, similar to your previous example but simplified:
typealias Emit<T> = (T) -> Unit
private object UNINITIALIZED
inline fun <T> loop(block: (Emit) -> Unit): T {
val returnValue: Any? = UNINITIALIZED
while (returnValue == UNINITIALIZED) block { returnValue = it }
return returnValue as T
}
Note however that the returnValue setter won't be inlined because the compiler can't inline higher-higher-order functions, if you will.ephemient
01/20/2022, 5:51 PMloop { ret ->
ret(1)
throw
}
throws instead of returning