> A PR was raised to make Arrow Fx Coroutines d...
# arrow
t
A PR was raised to make Arrow Fx Coroutines depend on KotlinX Coroutines to easy integration and usage since almost everyone has KotlinX Coroutines on the classpath anyway. More details in the PR Reviews and feedback welcome:Β https://github.com/arrow-kt/arrow-fx/pull/317
I noticed a little discrepancy in deprecation of
ForkConnected
here it should be
ReplaceWith("async(ctx) { f() }", "kotlinx.coroutines.Deferred")
The thing is, signature of
async
is
CoroutineScope.async(ctx...
. But
ForkConnected
itself doesn't need
CoroutineScope
. Was this intended, or is it an oversight?
s
Hey @than_, You're correct, and they're not exactly the same as you can see in the new implementation of
ForkConnected
since it violates the rules of structured concurrency.
Forking was already "discouraged" in favor of other functions such as
parMapN
,
parTraverse
etc Is there a specific use-case you have in mind?
Suggestions are also welcome! Feel free to comment on the PR directly as well. (That way it's easier to find it back in history)
t
I have this implementation for MVI pattern. My use case is basically fire and forget πŸ™‚.
dispatch
is suspended, so everything here happens in scope of the caller, so cancellation seems to work fine, actually everything seems to work well πŸ™‚. But maybe my understanding of coroutines is incomplete and I'm just missing some problem.
s
There is still support for fire-and-forget operators which is
GlobalScope.async { .. }
or
CoroutineScope(ctx).async { }
or
launch
. You could also use
coroutineScope
here if you want everything to be grouped together, but then
logAction
would be able to fail
dispatch
and the duration of
dispatch
would be equal to the longest-running task here. KotlinX Coroutines offers more variants on launching fibers which Arrow Fx offered, and also some more powerful ones for grouping lifecycles using
coroutineScope
. if you have any code pushed somewhere, I'd be happy to help you migrate πŸ™‚ Or answer any questions.
t
Don't worry πŸ™‚. It was just very convenient not having to pass scope everywhere, so I wondered what the catch was. I'm gonna dig into why
ForkConnected
breaks structured concurrency πŸ™‚. Thanks for your time.
s
Structured concurrency adds the property of awaiting all parallel ops to finish before it returns.
Copy code
coroutineScope {
  async { delay(1000)
  // <-- immediate return after fork
}

// Still takes 1000 since coroutineScope awaits async to finish
Copy code
coroutineScope {
   CoroutineScope(coroutineContext).async { delay(1000) }
  // <-- immediate return after fork
} // Returns immediately as well since here async is launched on a seperate scope
ForkConnected
is like the second one in that it returns immediately from
coroutineScope
but it still gets cancelled if it's parent scope gets cancelled.
Afaik it breaks structured concurrency in the sense that it doesn't await its scope. It doens't break it in the sense that it's lifecycle is still correctly coupled
It becomes a sibling of the outer scope, rather than a child ... πŸ€” That'd be a way to phrase it I guess πŸ˜„
t
Thanks for a quick explanation. This means that
ForkConnected
is basically
Copy code
inline fun <T> ForkConnected(
    ctx: CoroutineContext = ComputationPool,
    crossinline f: suspend () -> T,
): Deferred<T> =
    CoroutineScope(ctx).async { f() }
makes migrations easy πŸ™‚ Tought I knew how structured concurrency works. But why this gets correctly cancelled, I have no idea πŸ˜„
s
Copy code
inline suspend fun <T> ForkConnected(
    ctx: CoroutineContext = ComputationPool,
    crossinline f: suspend () -> T,
): Deferred<T> =
    CoroutineScope(coroutineContext).async(ctx) { f() }
It's like this actually, and it gets cancelled if
coroutineContext
contains a
Job
this
Deferred
will have the same
Job
attached and cancelling it will also cancel this
Deferred
.
t
OMG! I knew I remembered correctly, there is accessible context inside suspend πŸ˜„ Thank you very much! Now everything clicked together πŸ™‚
πŸ™Œ 2
s
Hey @Satyam Agarwal, you're right! This method would need to get deprecated in favor of an
Int
version! I created a ticket for it if you're up for it! πŸ˜‰ https://github.com/arrow-kt/arrow-fx/issues/324