kotlin-result
kotlin-result copied to clipboard
feat(parZip): add `parZip` functions for combining results of 2 to 5 computations in parallel
This pull request introduces a new parZip utility to the kotlin-result-coroutines library, which allows running multiple computations in parallel and combining their results. It also includes comprehensive test cases to validate the functionality of parZip for various scenarios.
-
Inspired by arrow-fx-coroutines parZip.
Regarding the name
parZip, I chose it by following the naming convention of theArrow.ktlibrary. If you have a better suggestion, I will update this PR accordingly 🙏. -
Added
parZipfunctions for combining results of 2 to 5 computations in parallel. If any computation fails, the others are cancelled, and the error is returned. -
Added
ParZipTestclass with test cases to verify the behavior ofparZipfor 2 to 5 computations.
Example
data class Movie(val id: Int, val title: String)
suspend fun getFavoriteMovies(): Result<List<Movie>, String> {
delay(100) // simulate network delay
return Ok(
listOf(
Movie(1, "Inception"),
Movie(2, "The Matrix")
)
)
}
suspend fun getPopularMovies(): Result<List<Movie>, String> {
delay(100) // simulate network delay
return Ok(
listOf(
Movie(3, "Avengers: Endgame"),
Movie(4, "Titanic")
)
)
}
suspend fun getTopRatedMovies(): Result<List<Movie>, String> {
delay(100) // simulate network delay
return Ok(
listOf(
Movie(5, "The Shawshank Redemption"),
Movie(6, "The Godfather")
)
)
}
// --------------------------------------------------------
data class HomePageMovies(
val favoriteMovies: List<Movie>,
val popularMovies: List<Movie>,
val topRatedMovies: List<Movie>
)
suspend fun getHomePageMoviesParZip(): Result<HomePageMovies, String> =
withContext(Dispatchers.IO) {
parZip(
{ getFavoriteMovies() },
{ getPopularMovies() },
{ getTopRatedMovies() },
) { favoriteMovies, popularMovies, topRatedMovies ->
HomePageMovies(
favoriteMovies = favoriteMovies,
popularMovies = popularMovies,
topRatedMovies = topRatedMovies
)
}
}
// Compare getHomePageMoviesParZip with getHomePageMoviesSequentially:
// The execution time of getHomePageMoviesParZip is significantly less than that of getHomePageMoviesSequentially.
// -> it will improve the app performance and user experience.
suspend fun getHomePageMoviesSequentially(): Result<HomePageMovies, String> =
withContext(Dispatchers.IO) {
coroutineBinding {
HomePageMovies(
favoriteMovies = getFavoriteMovies().bind(),
popularMovies = getPopularMovies().bind(),
topRatedMovies = getTopRatedMovies().bind(),
)
}
}
nice,
This operator is indeed necessary, but can we add a context parameter ctx: CoroutineContext = EmptyCoroutineContext, to adjust its behavior?
Thanks for this - I like this idea.
I am slightly uncomfortable with the implementation, as it seems to share a large amount of code with coroutineBinding itself, notably the creation of a coroutineScope, a custom ParZipException, etc.
I think I would prefer if the parZip implementation leveraged as much of the existing coroutineBinding function as possible, to reduce maintenance overhead and avoid introducing more custom exceptions.
I can imagine it will end up looking like the body (below) is wrapped in a coroutineBinding:
val values = producers
.map { producer -> async { producer().getOrThrow(::ParZipException) } }
.awaitAll()
Can you give this a go and let me know if the existing coroutineBinding doesn't work for you?
Can you give this a go and let me know if the existing
coroutineBindingdoesn't work for you?
Thanks. I've just updated the PR following your suggestion 👍.
Merged in b205cf2f06d06936d71edb2c19301b7f414c01ec, thanks!