Hello, I'm trying to get my mind around Arrow (0.1...
# arrow
a
Hello, I'm trying to get my mind around Arrow (0.11.0-SNAPSHOT), and trying to implement a simple app using its FP power. I have this class that retrieves Ids from a service, and then use theses Ids to get the details in parallel, then combine the result into a list to be used in the ui layer. The problem now once an error happens, it just crashes even though I'm handling things with Either
fold
. What I'm doing wrong in my case? and how can I prevent this code from crashing?
Copy code
class GetStoriesListUseCase @Inject constructor(
    private val hackerNewsService: HackerNewsService
) {

    suspend fun topStories(
        pageNumber: Int,
        pageSize: Int = 100
    ): Either<Throwable, List<StoryUIModel>> =
        readTopStoriesIds(pageSize)
            .flatMap { getTopStoriesDetails(it) }
            .suspended()

    private fun getTopStoriesDetails(topStoriesIds: List<Id<Long>>): IO<Throwable, List<StoryUIModel>> =
        topStoriesIds.map { it.value() }.parTraverse { anId ->
            IO.effect(<http://Dispatchers.IO|Dispatchers.IO>) {
                Timber.d("${Thread.currentThread().name}")
                hackerNewsService.topStoryDetail(anId).toStoryUIModel()
            }
        }

    private fun readTopStoriesIds(pageSize: Int): IO<Throwable, List<Id<Long>>> =
        IO.effect {
            hackerNewsService.topStoriesIds().map { Id.just(it) }.take(pageSize)
        }

    private fun StoryDetails.toStoryUIModel() =
        StoryUIModel(id = id, title = title, author = by, url = url)
}
a
Hi there @Ahmed Ibrahim! I assume it crashes on the
topStories
function?
a
Yep, that's correct, in case of network error, it crashes
a
it could be because you’re transforming an IO into a suspend, so if the IO you get has an exception (throwable, not E), it’s then mapped to how coroutines work, and they throw
an alternative in case you still want to use suspend is to do
someIO.attempt().suspended()
attempt does as follows
Copy code
IO<E, A>.attempt(): IO<E, Either<Throwable, A>>
so any fatal failure will be put into the either, but it still allows you to fold over your expected error E and the value
otherwise I’d recommend not converting IO to suspended in order to have more functionality
a
But I thought
IO<E, A>
was primarily introduced to replace doing IO work gracefully instead of working with Either?
a
yes, but the problem here AFAIK is that you’re narrowing the type from IO<E, A> to a suspend function, which allows A or throws
so the recommended way from the coroutines world seems to be to try-catch your code, because it throws
that’s why you either put the error information in the result (if you wanna use
suspended
) with attempt or you use IO directly
a
ah, I see, that's clear now, thanks for the explanation!
a
hope it helps! Don’t hesitate to ask any more questions/feedback you might have 🙂
a
So do you think that I should return instead an IO instead of Either, and do the
attempt().suspended()
in the client code? and catch the exception there through
fold
?
a
my very personal opinion is to execute the IO directly and don’t do
suspended
in general, but it really depends on the project and what you want to achieve
👍 1
using IO’s runtime has some advantages over the coroutines one that I’ll try to explain in the docs for the new version, but the most obvious one is that it doesn’t crash on errors
a
But one feedback that I have, is that it would be nice to have an
IO.catch { } -> IO<Throwable, A>
construct so that it might handle any error that could happen inside that block.
a
AFAIK that’s already embedded in IO, meaning that any exception that happens in its so to say “effectful constructors” (
IO.effect { }
,
IO { }
, etc) is gonna be kept, then you should be able to handle it or not with
handleError
, and finally execute it and obtain a value, a domain error or an exception
☝🏼 1
🔝 1
a
OK, now it is clear, thank you so much! One more question regarding the parallel execution of stuff, do you think what I'm doing through
parTraverse
is the best way to go, or there is a better alternative that I should look into?
👍 1
a
I think that’s good 👍
👍 1