What is the benefit to having methods like parTrav...
# arrow
c
What is the benefit to having methods like parTraverseEither / parSequenceEither be suspended functions since they are internally doing the work in parallel and then returning some result? Would wrapping the internals of that call in a
runBlocking
lambda have a negative impact on its behavior? Given the scenario that I want a method capable of taking in a list of strings and then resolving something via HTTP I seem to have a two options. 1. Make the method suspended, this requires callers to be suspended as well
Copy code
suspend fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = properties
        .parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
2 Make the method block so the callers don’t have to be suspended
Copy code
fun parRequireStrings(resolvers: List<String>): ValidatedNel<Error, List<String>> = runBlocking { 
  properties.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
}
The second option seems more desirable as you can call it outside of suspended functions, are there any drawbacks to this approach?
s
Hey @Cody Mikol, Yes, this has quite some drawbacks. More noticeable is that you turn a suspended effect (non-blocking) into a blocking one, so you effectively remove all benefits from
suspend
. In the second snippet, you're 100% going to be blocking the thread from where this is called (which can cause deadlocks, crashes on Android if called from main, etc). It's not advised to call
runBlocking
like this in places, but instead, you should try to call it only once on the edge of your program. From the docs "It is designed to bridge regular blocking code to libraries that are written in suspending style, to be used in 
main
 functions and in tests." So I'd say the first option is most desirable, besides the problems of
runBlocking
it's the only function that correctly models its behavior and is referentially transparent.
What is your use case so that the second option is most desirable? Java interop? In that case, you should use structured concurrency.
Copy code
scope.launch {
  val res = parRequireString(listOf(....))
  render(res)
}
c
Ahh, I think I had the misconception that wrapping a suspended function with
runBlocking
would just await the results of that suspension. Does this mean in the case of blocking
parTraverseEither
all parallel benefits are lost? I have a situation where I have a large batch of work to do and thought I could leverage this to run them in parallel. I’m currently working in a large application that uses the older blocking version of Spring Web REST controllers.
s
Does this mean in the case of blocking 
parTraverseEither
 all parallel benefits are lost?
Yes and no 😄 The problem is that a server runs the incoming request on a thread pool, whenever you run into
runBlocking
it blocks one of those threads until the
runBlocking
is finished. If the pool is size 8, and you get 8 incoming requests that all run into
runBlocking
then the server will not be able to handle any more incoming requests. So that can heavily degrade the throughput of a server. In contrast, using
suspend
will not block any thread but jump between threads and that'll will allow other incoming request to be accepted will the other request are running some work in parallel in the background. So to bridge between
suspend
and the Java world you should utilize one of the Java features that have the same capabilities. With Spring Web REST controllers that would be
CompletableFuture
, so you can use the KotlinX Coroutines JDK8 support to bridge. https://github.com/Kotlin/kotlinx.coroutines/tree/master/integration/kotlinx-coroutines-jdk8
Copy code
future {
properties.parTraverseValidated(Semigroup.nonEmptyList()) { requireString(it).toValidatedNel() }
}
If you're using Spring WebFlux you could use the Reactor integration instead: https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/kotlinx-coroutines-reactor