Created
March 1, 2026 18:48
-
-
Save kyay10/c7e25149ddad21410e2ec93028d804f4 to your computer and use it in GitHub Desktop.
Kotlin first-class inline functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import kotlin.coroutines.Continuation | |
| import kotlin.coroutines.EmptyCoroutineContext | |
| import kotlin.coroutines.RestrictsSuspension | |
| import kotlin.coroutines.cancellation.CancellationException | |
| import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED | |
| import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn | |
| import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn | |
| private fun main() { | |
| val d: Decorator<Unit, Unit, Unit> = { | |
| test { iteration(it) } | |
| } | |
| d { | |
| return | |
| } | |
| } | |
| private inline fun test(block: (Unit) -> Unit) { | |
| repeat(3) { | |
| try { | |
| block(Unit) | |
| } finally { | |
| return@repeat | |
| } | |
| } | |
| } | |
| private inline fun calculate(transform: (Int) -> String): String = | |
| try { | |
| (1..5).map { | |
| var failed = true | |
| try { | |
| transform(it) | |
| failed = false | |
| } finally { | |
| if (failed) transform(it + 1) | |
| } | |
| }.joinToString() | |
| } finally { | |
| println("releasing resources") | |
| } | |
| public typealias Decorator<T, R, S> = suspend DecoratorScope<T, R>.() -> S | |
| public inline operator fun <T, R, S> Decorator<T, R, S>.invoke(block: (T) -> R): S = | |
| with(DecoratorCoroutine<T, R, S>()) { | |
| (beginDecorator(this@invoke) ?: provideIteration(block) { | |
| provideIteration(block) { | |
| provideIteration(block) | |
| } | |
| }).getOrThrow() | |
| } | |
| @PublishedApi | |
| internal inline fun <T, R, S> DecoratorCoroutine<T, R, S>.provideIteration( | |
| block: (T) -> R, | |
| handleFinally: () -> Result<S> = { TODO("Edgecase: calling `iteration` in `finally` that's caused by `iteration`") } | |
| ): Result<S> { | |
| var result: Result<S>? = null | |
| while (result == null) { | |
| var resultSet = false | |
| try { | |
| result = provideResult(Result.success(block(value))) | |
| resultSet = true | |
| } catch (e: Throwable) { | |
| result = provideResult(Result.failure(e)) | |
| resultSet = true | |
| } finally { | |
| if (!resultSet) { | |
| val exc = CancellationException() | |
| result = provideResult(Result.failure(exc)) | |
| if (result == null) result = handleFinally() | |
| if (result.exceptionOrNull() !== exc) continue | |
| } | |
| } | |
| } | |
| return result | |
| } | |
| /** | |
| * The [decorator] suspends for the iteration so that the one lambda can be run as | |
| * two separate parts, without needing to wrap an inlined block. | |
| */ | |
| @PublishedApi | |
| internal class DecoratorCoroutine<T, R, S> : Continuation<S>, DecoratorScope<T, R> { | |
| private var continueAfterIteration: Continuation<R>? = null | |
| var value: T = null as T | |
| private set | |
| private var ultimateResult: Result<S>? = null | |
| override suspend fun iteration(param: T): R { | |
| check(continueAfterIteration == null) { | |
| "Iteration function somehow referenced itself!" | |
| } | |
| return suspendCoroutineUninterceptedOrReturn { | |
| continueAfterIteration = it | |
| value = param | |
| COROUTINE_SUSPENDED | |
| } | |
| } | |
| @PublishedApi | |
| internal fun beginDecorator(decorator: Decorator<T, R, S>): Result<S>? { | |
| check(ultimateResult == null) { "Decorator already completed" } | |
| return runCatching { | |
| val res = decorator.startCoroutineUninterceptedOrReturn( | |
| receiver = this, | |
| completion = this | |
| ) | |
| if (res == COROUTINE_SUSPENDED) return null | |
| res as S | |
| } | |
| } | |
| @PublishedApi | |
| internal fun provideResult(value: Result<R>): Result<S>? { | |
| check(ultimateResult == null) { "Decorator already completed" } | |
| val cont = continueAfterIteration ?: error("Iteration not invoked") | |
| continueAfterIteration = null | |
| cont.resumeWith(value) | |
| return ultimateResult.also { ultimateResult = null } | |
| } | |
| override val context get() = EmptyCoroutineContext | |
| override fun resumeWith(result: Result<S>) { | |
| ultimateResult = result | |
| } | |
| } | |
| @RestrictsSuspension | |
| public sealed interface DecoratorScope<T, R> { | |
| public suspend fun iteration(param: T): R | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment