Hello, I have a question about the `suspendCorouti...
# coroutines
r
Hello, I have a question about the
suspendCoroutine
function. Given the following code, it prints "Begin End Hello". I believe this behavior is expected because, according to the KEEP,
suspendCoroutine
only suspends when the block returns without invoking
cont.resumeWith
. However, this behavior seems incompatible with the documentation's description: "Obtain the current continuation instance." to me.
Copy code
val hello = suspendCoroutine { cont ->
    println("Begin")
    cont.resume("Hello")
    println("End")
}
println(hello)
https://play.kotlinlang.org/embed?short=JJBf260zy Anyway here is my question: can anyone suggest a way to consistently obtain the current continuation? It is required to go well with
kotlinx.coroutines
. I think we can utilise the intrinsic APIs but I would prefer to explore other options before resorting to it. It would behave like this:
Copy code
val hello = ourSuspendCoroutine { cont ->
    println("Begin")
    cont.resume("Hello")
    println("End")
}
println(hello)
// => Begin Hello End
y
This would require
resume
to be
suspend
very likely. Also, another question arises, which is when do you expect the
resume
to be called back? Should it be when the coroutine is completely done, or do you expect it to return when a specific level finishes? One simple thing you could do is use something like
ResourceScope
from Arrow:
Copy code
resourceScope {
  val hello = run {
    println("Begin")
    onResume { println("End") }
    "Hello"
  }
  println(hello)
}
// => Begin Hello End
r
Thank you for your response! Yes, I'm pretty sure it would have a
suspend
block as a parameter. Perhaps what I want can be like this:
suspend fun callCC(block: suspend (suspend (T) -> Nothing) -> T): T
. And I want it to behave like
suspendCoroutine
, while I am not currently sure how it behaves. Also, thank you for your suggestion, I'll try
ResourceScope
and check how
ResourceScope
is implemented.
y
If you're coming from another language with continuations then yes that's what you'd expect. You might wanna check out Kontinuity, an implementation of (multishot, multiprompt) delimited continuations, which offers
shift
and
reset
which you might be familiar with from other languages (like Racket). It's still a WIP, but I'm fairly confident that it has the correct behaviour. Your use case would look something like:
Copy code
newReset {
  val hello = shift { k ->
    println("Begin")
    k("Hello")
    println("World")
  }
  println(hello)
}
👀 1
❤️ 1
1
suspendCoroutine
is indeed meant as a
callCC
-style function, but instead of short circuiting on continuation application, it returns
Unit
instead (and, of course, it's single shot)
r
I’m quite surprised that the library also supports multi-shot continuations, even though it’s said that it can be implemented in KEEP. Actually, I was trying to implement shift/reset to make it compatible with asynchronous APIs in
kotlinx.coroutines
, so that I can have shallow-nested effect handlers when context parameters are supported. Thank you for sharing the information.
The library also has effects! 🥳
y
(I should do a write up explaining how the library works) It's just using a tiny bit of reflection to be able to shallow-clone continuation frames. It currently only works for JVM and JS because I can't figure out a native implementation of such cloning. Theoretically though, this is a very very simple addition that the Kotlin compiler could add (it's basically 2 functions, one to access the parent of a frame, and another to shallow copy a frame and give it a new parent. The first can be exposed in stdlib easily. The second needs a very simple clone method on every compiler-generated continuation class) Sadly, it seems that a compiler plugin wouldn't be able to generate these functions because the continuation classes don't yet exist at the phases that the compiler calls plugins at. Such continuation classes are created basically right at the very end, so reflection it is for now. The library indeed has effects! That was the main motivation for its existence. I suggest you have a look at the test directory, particularly the
effekt
tests. It's delightful how expressive all the examples are! The library provides both deep and shallow effect handlers. The Handler interface I think only provides deep ones, but that's because the examples I'm using haven't needed shallow ones yet (I think it's very trivial to get shallow handlers just by using 2 prompts or something). That's like a one liner though to expose a shallow version of
Handler.use
I'd love to hear any feedback you have if you end up tinkering with it!
🙌 1
r
It is quite great to see these details. As expect, it requires reflections or compiler plugins to clone continuation, although it is mentioned in KEEP. I thought it would be nice if I could do it with intrinsics but it was not as easy as it seems.
And about effects, the examples are surely beautiful. It was great to talk with you, thank you again.