Conversation
| } | ||
| } | ||
|
|
||
| final class Await[F[_]] private[direct] (private[direct] val scope: ContinuationScope) { |
There was a problem hiding this comment.
We could even extends ContinuationScope but maybe don't want to leak that?
There was a problem hiding this comment.
Yeah I would rather not leak it, especially if they change the API.
|
|
||
| final class Await[F[_]] private[direct] (private[direct] val scope: ContinuationScope) { | ||
| private[direct] var next: F[Any] = _ | ||
| private[direct] var result: Any = _ |
There was a problem hiding this comment.
@jducoeur raised a good point about error handling. We should also have a channel for Throwables ...
| private[direct] var result: Any = _ | |
| private[direct] var result: Any = _ | |
| private[direct] var error: Throwable = null |
| await.next = self.asInstanceOf[F[Any]] | ||
| Continuation.`yield`(await.scope) | ||
| await.result.asInstanceOf[A] |
There was a problem hiding this comment.
... that is thrown like this:
| await.next = self.asInstanceOf[F[Any]] | |
| Continuation.`yield`(await.scope) | |
| await.result.asInstanceOf[A] | |
| await.next = self.asInstanceOf[F[Any]] | |
| await.error = null | |
| Continuation.`yield`(await.scope) | |
| val e = await.error | |
| if (e ne null) throw e else await.result.asInstanceOf[A] |
|
One challenge that I realized this morning with this approach: auto-ceding. In theory it should work since we're only crossing thread boundaries while the continuation is suspended, but it needs some testing. Additionally, the semantics here may be a bit different between JVM and LLVM. |
|
The Scala Native version is Scala 3 only, which is a little frustrating since it doesn't need to be. Additionally, it doesn't work. I keep getting the following error: |
|
@djspiewak About the SN problem: I definitely don't understand, how this magic works, but this code seems suspicious to me: private[effect] def asyncImpl[F[_]: Sync, A](body: Await[F] => A): F[A] =
boundary[F[A]] {
Sync[F].delay {
val await = new Await[F] {
type Result = A
val label = implicitly[BoundaryLabel[F[A]]]
}
body(await)
}
}This seems to return an
Which is exactly what |
|
@durban Oh it's a lazy vs eager execution issue! That makes a lot of sense. So then this encoding just isn't going to work. I'll ponder that a bit more. I think it's quite ironic that the typeful and more functional encoding of this concept (in Scala Native) is harder to work with for this goal than the imperative and untyped version in the JVM. |
|
I think what we've concluded is that I like long jumps and I cannot lie. |
This basically yeets out the existing compile-time mechanism and replaces it with one based on
Continuation(the JVM basis for virtual threads). Note that this requires some runtime shenanigans from users, but it's not too bad and the benefits are quite significant (e.g. no more issues with higher order functions or other transformation failures).Haven't implemented yet for Scala Native (we'll probably use something like this https://dl.acm.org/doi/10.1145/3679005.3685979) or Scala.js (idk @armanbilge had some idea here) but it should be possible in principle.