simon.vergauwen
02/28/2023, 8:35 PMparTupled
and it couldn't be resolved without providing CoroutineContext
🤔 Did you encounter something like this while you were working on the PR?julian
02/28/2023, 9:21 PMCoroutineContext
. I don't know how that even happened.
I didn't encounter the problem because I wasn't looking for it 😅. I only implemented the `parTupled`s that require a CoroutineContext
, or in absence of one, force named parameters to be used.
Sorry about that.
I can provide another PR for the missing functions.simon.vergauwen
03/02/2023, 2:11 PMparZip
accidentally overwrites <http://Dispatchers.IO|Dispatchers.IO>
. I.e.
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
parZip({ io() }, { otherIO() }) { a, b -> ... }
}
While in contrast that doesn't happen with other functionality in KotlinX, so it's not really in line with each-other.
So all the Dispatchers.Default
overloads should actually be deprecated. They should be needed at all..
Curious if this improves the situation with parTupled
... Or the overloading syntax with Tuple
.. I personally think overloading parZip
with 18 methods just for tupling is a tad much 😞 I was already a bit on the fence when it was 9 methods.parZip
and parTupled
😕 Pfft that sucks..ctx: CoroutineContext = EmptyCoroutineContext
.julian
03/02/2023, 5:23 PMparTupled
to this sort of thing:
public suspend inline fun <A, B> parTupled(
crossinline fa: suspend CoroutineScope.() -> A,
crossinline fb: suspend CoroutineScope.() -> B,
): Pair<A, B>
parTupled
?simon.vergauwen
03/02/2023, 5:55 PMcoroutineScope { awaitAll(async { }, ..., ...) }
. I have to periodically remind/re-check myself why we have these methods 😅
TL;DR provide awaitAll
behavior over suspend fun
, which is consistent across Job
and SupervisorJob
. Running a bunch of different tests now, and I am again running in some unexpected scenarios 😅 I guess parZip
provides a simpler model for parallelism.. I need to write a long document on this, I keep forgetting details after not working with low-level KotlinX for a long time 🤕suspend fun one(): Int = delay(1000).let { 1 }
suspend fun two(): Int =
throw RuntimeException("Boom!")
coroutineScope {
measureTimedValue {
kotlin.runCatching {
val fa = async { one() }
val fb = async { two() }
fa.await() + fb.await()
}
}.let(::println)
}
I was expecting that runCatching
here captures RuntimeException
but the first await
throws JobCancellationException
, and then coroutineScope
rethrows the RuntimeException
.
Whereas:
coroutineScope {
measureTimedValue {
kotlin.runCatching {
parZip({ one() }, { two() }) { a, b -> a + b }
}
}.let(::println)
}
Here I can capture the RuntimeException
since awaitAll
(used in parZip
) throws RuntimeException
and cancels-and-joins the others async
internally first.coroutineScope
with supervisorScope
then the supervisorScope { async { } }
snippet takes 1.second
instead of failing fast. Which is expected by supervisorScope
but parZip
provides consistent semantics in this case again through awaitAll
.
Although this is expected of supervisorScope { }
when a function is defined suspend fun CoroutineScope
(which is actually illegal to have both suspend
and CoroutineScope
) the behavior depends on the caller.parZip
though 🤔 I use it frequently, but that's probably because I am biased and used to it 😂 It's also consistent to what I know from other FP langs.julian
03/02/2023, 6:14 PMwhen a function is defineddo you mean an extension function withsuspend fun CoroutineScope
CoroutineScope
as receiver?simon.vergauwen
03/02/2023, 6:15 PMCancellationException
is thrown. Then it's prone to hanging in some cases but SupervisorJob
can also cause that in some edge-cases.julian
03/02/2023, 6:20 PMparZip
much, myself. But I'm probably not the typical Arrow user - I don't get to use Arrow at my job. I don't really have a strong position. Nor a strong grasp of the trade-offs, if I'm being honest.simon.vergauwen
03/02/2023, 6:22 PMparTupled
for now. We can always re-raise your PR and add it later. Removing it later is harder.
Well, you not using Arrow makes that you have good insight and might be able to provide good feedback here. Did you ever encounter any of the problems I described above, while working or in production and would you rather use parZip
?julian
03/02/2023, 6:28 PMI really hate throwing people's work and effort awayThank you for caring. It's fine, though. Sometimes one must do a thing to really know what a thing means in context. That's just life, right? 😄
simon.vergauwen
03/02/2023, 6:34 PMjulian
03/02/2023, 6:35 PMsimon.vergauwen
03/02/2023, 6:38 PMjulian
03/02/2023, 6:40 PMparZip
if I was unsure about how awaitAll
behaves relative to coroutineScope
vs supervisorScope
. Or I was pressed for time and didn't want to have to experiment to find out. But folks more expert in coroutines might opt to be more explicit/transparent and skip parZip
in favor of awaitAll
.we're moving the meeting to 4pm CESTThat's great news! 🥳 Thank you all!
simon.vergauwen
03/02/2023, 6:46 PMEspecially if they're confident of the expertise of their code-reading audience.I guess it's a trade-off between more low-level vs external library... More flexibility vs less error-prone. Thread/Executor vs ReactiveX/IO
parTupled
when I was writing new documentation and working on the KotlinConf workshop. Super curious what you think about this API. If you're interested on working on this I'd be super happy to help and guide you ☺️
https://github.com/arrow-kt/arrow/pull/2957julian
03/03/2023, 3:06 PM