I have a `List<Either<CutomErrorType, Unit&g...
# arrow
j
I have a
List<Either<CutomErrorType, Unit>>
and I want to transform it into a
Either<List<CustomErrorType>, Unit>
, how can I do that? I know
traverse
will give me a
Either<CustomErrorType, List<Unit>>
but I need a sort of
traverseLeft
that doesn’t seem to exist.
y
Maybe
.map(Either::swap).traverse()
s
Copy code
mapOrAccumulate { it.bind() }
That should work, right?
It should return
Either<NonEmptyList<CustomErrorType>, List<Unit>>
but you can swallow
List<Unit>
into
Unit
y
Speaking of mapOrAccumulate, you should likely take a step back and see if the
List<Either<CustomErrorType, Unit>>
is even needed, or if your code could be restructured into running all those error-producing functions inside of
mapOrAccumulate
and perhaps even using a Raise receiver instead of returning that Either
j
Is
mapOrAccumulate
available in 1.2.0 and above? I haven’t migrated yet, I guess it’s time hehe.
s
Yes, it's available since 1.2.0
j
mapOrAccumulate
transforms my
Either<CustomErrorType, List<Unit>>
into a
Either<NonEmptyList<CustomErrorType>, List<List<Unit>>>
so now I need to figure out how to flatten the “right” result.
Regarding what I’m trying to do : I’m fetching a list of ids and for each one of them I fetch a corresponding list of user profiles. Since each request can fail, I end up with a list of
Either<Error, List<UserProfile>>
. At first I was saving all the freshly fetched lists of profiles in database, thus returning
Either<List<CustomErrorType>, Unit>
with
Unit
if all the insertions went fine. But I took it out to simplify that part of the code a bit.
s
Untitled.kt
Small suggestion: Arrow Fx Coroutines has
parMapOrAccumulate
which combines parallel mapping and
mapOrAccumulate
in a single operation whilst hiding all the
CoroutineScope
,
async/await
mechanics. This also fixes the bug where you have to use
.awaitAll
instead of
map { it.await() }
. It also makes
fetchUserProfileAsync
a bit simpler since it's no longer aware it runs
Async
. It's just a simple
suspend
definition, and the caller decides how it runs. To flatten, the easiest thing is just to use
map { it.flatten() }
, perhaps we should investigate
flatMapOrAccumulate
for future versions. I've seen this
map { it.flatten() }
come up a couple times now.
To add the suggestion from @Youssef Shoaib [MOD], you can also use
Raise
but let me share a separate snippet.
Untitled.kt
This snippet is not crazy different, the
Either
is replaced by an extension on
Raise<Error>
and
bind()
disappears. There is no longer a need to wrap in
right
and when you wrap in
left
you instead call
raise(x)
.
Take or leave that last snippet based on preference of code style ☺️ All code can be written in either (🥁) style
j
Nice! I wondered what “par” meant in the APIs, now I have an answer for that too. I’m not familiar with the
Raise
type yet, but it look quite similar to Either with less boiler plate. Thanks for the help @simon.vergauwen!
s
My pleasure ☺️ Feel free to ask any question you have. There is a pretty nice page on the website about it too, https://arrow-kt.io/learn/typed-errors/working-with-typed-errors/. In practice whichever you and your team feel more comfortable with work just as well. It's just a different style.
Raise
will become more powerful with context receivers, since you can do some things that
Either
cannot do like compose two errors.
context(Raise<Error1>, Raise<Error2>)
.