import arrow.Kind import arrow.effects.ForIO import arrow.effects.IO import arrow.effects.extensions.io.async.async import arrow.effects.fix import arrow.effects.typeclasses.Async import io.ktor.application.ApplicationCall import io.ktor.application.call import io.ktor.response.respond import io.ktor.routing.Routing import io.ktor.routing.get import io.ktor.routing.routing import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine interface Deferrable { fun Kind.deferred(): Deferred } fun IO.Companion.deferrable(): Deferrable = object: Deferrable { override fun Kind.deferred(): Deferred { val io = this.fix() return runBlocking { async { val result: A = suspendCoroutine { cont -> io.unsafeRunAsync { result -> result.fold(cont::resumeWithException, cont::resume) } } result } } } } suspend fun ApplicationCall.respondWithDeferrable(value: Kind, deferrable: Deferrable) { val output: Any = deferrable.run { value.deferred() }.await() respond(output) } class DemoService(private val async: Async) { fun makeSomeWork(): Kind = async.delay { "some work" } } class DemoRouting(private val service: DemoService, private val deferrable: Deferrable) { fun Routing.demo() { get("/demo") { val work: Kind = service.makeSomeWork() call.respondWithDeferrable(work, deferrable) } } } fun main() { val deferrable: Deferrable = IO.deferrable() val service: DemoService = DemoService(IO.async()) val demoRouting: DemoRouting = DemoRouting(service, deferrable) val server = embeddedServer(Netty, port = 8080) { routing { with (demoRouting) { demo() } } } server.start(wait = true) }