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 Int
Jason5lee
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 Kotlininterface 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