Hi, I'm trying to get my head around using IO (I'm...
# arrow
k
Hi, I'm trying to get my head around using IO (I'm "leveling up" from simply using Either 😛 ). I'm a bit stuck with how/when to actually execute my IO. For example, let's say I have a retrofit api with this signature:
Copy code
fun getStore(): CallK<StoreDto>
. On the repository layer, I'd like to obtain a Store, but as it might come from the network, I guess it should be wrapped in an IO like so:
Copy code
fun getStore(): IO<Store>
. My problem resides in the fact that I don't find where I should do my mapping from StoreDto to Store. I have something like this for now:
Copy code
kotlin
        apiCall.async(IO.async())
            .fix()
            .unsafeRunAsync { either ->
                either.fold( // <-- the result of this is what I'd like to get
                    { IO.raiseError<Store>(it) },
                    { retrofitResponse ->
                        retrofitResponse.unwrapBody(Either.applicativeError()).fix().fold(
                            { IO.raiseError(it) },
                            { storeDto -> IO.just(Store(storeDto.name)) }
                        )
                    }
                )
            }
But this actually execute the code at the repository level, and doesn't return my expected type. I'd like to actually execute this at a higher level, say my android activity in my presentation layer. Any tips on what direction I should take? I'm trying to get inspiration from https://github.com/JorgeCastilloPrz/ArrowAndroidSamples , but I don't understand all that is done there at the DataSource level…
r
Esentially you should never call
unsafeRunAsync
just use
IO
in the return type of your functions
what you are doing there it's just:
Copy code
apiCall.async(IO.async()).fix().attempt()
use map, flatMap or fx
to transform the contents of IO but never abandon it
unsafeRunAsync belongs in a single place at the edge of your app. main, the controller or the activity but never at this network layer
k
@raulraja Hmm, doc said about attempt : "Executes and defers the result into a new IO", I thought it worked pretty much the same way as unsafeRunAsync
r
attempt never executes
that is wrong, should be fixed then
attempt
just maps inside IO an attempt operation
and it preserves your program pure
but unsafeRunners wont
they will break purity
@Jorge Castillo those examples should be updated because many users come encouraged by using unsafeRunAsync at the apiCall level
👍 1
k
perfect, that's what I needed! I'll try to make it work first, then I'll try to understand how to use fx {} 😉
r
essentially the rule of thumb is everything returns IO
never call any unsafe method until the edge of the app
use all others map, flatMap,
!
etc.
if yo use fx you can make your programs imperative and pure
async and concurrent stuff is also trivial
k
Hmm, I'm almost there, but I now have an
IO<IO<Store>>
... What am I missing?
Copy code
apiCall.async(IO.async())
            .fix()
            .attempt()
            .map { either ->
                either.fold(
                    { IO.raiseError<Store>(it) },
                    { it.unwrapBody(IO.applicativeError()).fix().map { storeDto -> Store(storeDto.name) } }
                )
            }
k
If your map Returns another IO, you can Change map to flatMap
k
Wow, I was so headstrong in those IO problematics that I forgot these "simple" methods, thanks @kartoffelsup
r
You can also just:
Copy code
apiCall.async(IO.async()).map { Store(it.name) }
why do you attempt first if you are gonna raise the error inmediately?
just use map which goes over the happy path since you seem to be ignoring the Throwable side if any in all cases and just reraising
k
Haha, I did it because I'm a noob ^^
Alright, I'll leave you be for a week, I'm on vacation, thanks for your help 😉
r
no problem! feel free to ask any time!
s
I’m here to just follow this thread! Very interesting
👍 2
m
IO seems like a natural extension of Try, as it uses Throwable for the ‘left’ side. So how does one use IO but with a custom left? My services are using Either<Notification, T> where Notification is our error class, and we return that to services, completely avoiding exceptions for error handling. We reserve exceptions for truly exceptional. I haven’t seen examples doing that, so am I approaching the problem incorrectly, or is there something I’ve missed?
r
We are working on bifunctor IO right now which does that
It will be available after 0.9.1
👍 1
m
Ahhh, that explains it. Thank you.
👍 1
j
I'm also aiming to use BIO<E, A> (for Android in my case) as soon as it becomes available, since it's a highly common use case in that platform
I'll talk about BIO, also RIO<R, E, A> and overall effects through ArrowFx in Kotliners conference in June
👏 3
arrow 1