Hei Hei. So I am trying to make a colleague unders...
# arrow
s
Hei Hei. So I am trying to make a colleague understand
IO<A>
. I thought of starting with this line :
IO<A>
is just a another way of writing
suspend () -> Either<Throwable, A>
is this true ? I grasped this from the PR Simon worked on to bring features from IO to Either using coroutines so that we don’t have to box our types which apparently is costly. I know IO has its own thread pool, works on fibres and not coroutines, but I was wondering if the above statement could be a layman’s explanation ?
s
Hey @Satyam Agarwal, That's almost correct.
IO<A>
~>
suspend () -> A
, since
suspend
encapsulates
Throwable
. If you try to run
suspend () -> A
, you always need to provide a function
(Result<A>) -> Unit
in
startCoroutine
when constructing a
Continiuation<A>
. Similarly to
IO
which exposes a function
unsafeRunAsync
which takes a function
(Either<Throwable, A>) -> Unit
. So it's clear these two are the same. Building
IO
is more expensive than using
suspend
since
IO
is build at runtime, and
suspend
is generated by the compiler at compile time. This costs becomes neglectable in heavy concurrent applications with a lot of IO operations, and it becomes more expensive in loops for example.
s
Awesome. Thank you so much 🙂
s
The
IO
module exposes a set of utilities like
Fiber
,
Resoure
,
Schedule
, dispatchers. and much more to work with
IO
. In the last year I've been working hard on trying to figure out how we could translate all these same principles and patterns from
IO
to
suspend
without losing any features or capabilities. Which finally landed in the PR you probably saw already. The same readme can be found here: https://github.com/arrow-kt/arrow-fx/tree/master/arrow-fx-coroutines
You're very welcome @Satyam Agarwal! If you have any more questions or feedback I'd love to hear it 🙂
s
Actually. Its related to general async and non-blocking stuff. In my team we are now heavily using ktor with Arrow, and people coming from javascript are writing code in arrow, which is absolutely amazing. They are able to do it without knowing almost anything about these concepts. Since I’ve been writing webapps in arrow for some time now, I’ve found a pattern that is pretty good, and these colleagues just look for examples around these webapp, and mostly it just comes to copy paste. But time to time, when edge cases come, we dive into theses concepts, and then I find myself in a situation where I know how things work and how to play with them, but it becomes hard for me to put them into words to explain to my colleagues. I will paste one such example and would love to hear what you think about it and get your feedback at how can I be better on it.
s
Please do! I'd be happy to clarify any doubts you have in your teams! It's always helpful to supplement docs, or improve APIs 👍
s
@simon.vergauwen Here I trying to explain to my colleague why I converted my ktor method
fun Application.myModule() : Unit
to
suspend fun <http://Application.my|Application.my> module() : Unit { ... myUnsafeIOWrapped.suspendedCancellable() }
Also he asked about blocking and non-blocking.
s
Yes, that is absolutely correct. I think the biggest problem is that some of the terms aren't well defined in our field, and they often differ a little bit between libraries etc. For example, if we talk about blocking on the JVM we typically talk about operations such as
Thread.sleep
which actually block a thread. Which is problematic on a pool such as
ComputationPool
,
IO.dispatchers().default()
or
Dispatchers.Default
, since those pools are "work-stealing". Meaning they interleave computational work, which is not possible if an operation is blocking the thread. (TL;DR Blocking doesn't allow interleaving of tasks) So a blocking task should be scheduled on an "IO" pool, which creates a new thread per operation. Hence blocking is not a problem, since no interleaving happens on this pool.
IOPool
,
IO.dispatchers().io()
,
<http://Dispatchers.IO|Dispatchers.IO>
, etc. The part about
IO
and
suspend
could be argued about perhaps, since on a back-end requests are running in parallel there is always asynchrony involved. What makes both
IO
and
suspend
so special is that they can wrap a
callback
which regular code cannot. Being able to wrap a callback allows you to jump to a different scheduler, and back, without having to write any of the orchestration around it like you normally would with callbacks.
Copy code
val result: Result? = ...

runOnComputation { result ->
   runOnIO(result) { res2 ->
      runOnComputation(res) { res3 ->
          finished, return result (???)
          result = res3
      }
    }
}

use result??
compared to
Copy code
val res = runOnComputation()
val res2 = runOnIO(res)
val res3 runOnComputation(res2)
Where you can implement
runComputation
with
suspendCoroutine
like you could do with
IO.async
.
Where or how they run can still be completely dictated by the caller, as you mention, this is Ktor in this case. And it can run our functions using
parTraverse
or
parTupled
or run them synchronously depending on the config we passed to Ktor. Similarly you can make functions
uncancellable
by wrapping them in
uncancellable { }
. Even if they contain cancellable code.
s
Wow. Thank you so much. I can basically copy paste this so that my team can understand these things even more. Thank you 🙂
🙌 1