Kotlin - Composition of multiples IO - kotlin

I'm new to Kotlin's Arrow Framework and I have a couple of questions:
Lets suppose
fun getUser(id: Int): IO<Option<User>>
fun getCards(user: User): IO<List<Card>>
fun getUserAndCards(id: Int): IO<Option<Pair<User, List<Card>>>> = IO.fx {
when (val user = !userRepository.get(id)) {
is None -> None
is Some -> {
val cards = !cardRepository.get(user.t.id)
Some(Pair(user.t, cards))
}
}
}
How can I achieve the same functionality in an "arrow stylish" manner?
I manage to get:
fun getUserAndCards(id: Int): IO<Option<Pair<User, List<Card>>>> = IO.fx {
userRepository.get(id).bind().map { user ->
val cards = cardRepository.get(user.id).bind()
Pair(user, cards)
}
}
But I obtain Suspension functions can be called only within coroutine body in the second bind().
EDIT:
I saw this post with the same question. In the answer provided, it says The problem is that the left/none option isn't covered. But IT IS covered, when applying map to a None it is expected to obtain a None.

With the new 0.11.0 release coming up soon, the most idiomatic way would be to use Arrow Fx Coroutines.
Rewriting the example to Arrow Fx Coroutines would be:
suspend fun getUser(id: Int): Option<User>
suspend fun getCards(user: User): List<Card>
suspend fun getUserAndCards(id: Int): Option<Pair<User, List<Card>>> =
option {
val user = !userRepository.get(id)
val cards = !cardRepository.get(user.t.id)
Pair(user.t, cards)
}
Where you can now rely on a option { } DSL to extract the values from Option.
The problem is that the left/none option isn't covered. But IT IS covered, when applying map to a None it is expected to obtain a None.
You're correct that it's covered, but ! is a suspending function, and map is currently not inlined so you're not allowed to call ! inside. In the 0.11.0 release the operators from the data types in Arrow-Core are inline, to improve support for suspend functions and this would solve the Suspension functions can be called only within coroutine body error.
In other functional languages such as Haskell monad transformers are often used (OptionT), but in Kotlin using suspend is a much better fit which also has quite some performance benefits over wrapping monad transfomers.
As mentioned in the other post, you can also always use traverse or sequence to turn two containers around. Option<IO<User>> -> IO<Option<User>>

Related

In Kotlin, how do I convert "CompletableFuture<Optional<T>>" to "Flow<T?>"?

I'm trying to convert a CompletableFuture<Optional<T>> to a Flow<T?>. The extension function I'm trying to write is
fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> =
this.toMono().map { (if (it.isPresent) it.get() else null) }.asFlow()
but it fails because asFlow() doesn't exist for nullable types, AFAICT based on its definition.
So, how do I convert CompletableFuture<Optional<T>> to Flow<T?>?
Edit 1:
Here's what I've come up with so far. Feedback appreciated.
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import java.util.Optional
import java.util.concurrent.CompletableFuture
fun <T> Optional<T>.orNull(): T? = orElse(null)
fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> = flowOf(this.join().orNull())
FYI, in my case, which is using Axon's Kotlin extension queryOptional, I can now write this:
inline fun <reified R, reified Q> findById(q: Q, qgw: QueryGateway): Flow<R?> {
return qgw.queryOptional<R, Q>(q).asFlowOfNullable()
}
I'll defer for a while creating a comment with the above pattern as the answer to allow for feedback.
Edit 2:
Since it was pointed out below that asFlowOfNullable in Edit 1 would block the thread, I'm going with this from #Joffrey for now:
fun <T> Optional<T>.orNull(): T? = orElse(null)
fun <T> CompletableFuture<Optional<T>>.asDeferredOfNullable(): Deferred<T?> = thenApply { it.orNull() }.asDeferred()
Edit 3: credit to both #Tenfour04 & #Joffrey for their helpful input. :)
To use the below extensions, you need the jdk8 coroutines library:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$1.5.0"
I'm not sure where the asFlow() function comes from that you're using, but here's a way I think would work without it. It seems a little odd to me to have a Flow of a single item, because it could just be a suspend function or if you need it as an object to pass around, a Deferred, which is intended for returning a single result and is therefore more analogous to a Future than a Flow.
fun <T> CompletableFuture<Optional<T>>.asFlowOfNullable(): Flow<T?> =
flow { emit(await().orElse(null)) }
As a suspend function:
suspend fun <T> CompletableFuture<Optional<T>>.awaitNullable(): T? =
await().orElse(null))
As a deferred:
fun <T> CompletableFuture<Optional<T>>.asDeferredNullable(): Deferred<T?> =
thenApply { it.orElse(null) }.asDeferred()

Run Kotlin coroutines in sequence

Lets say I have these Kotlin suspend functions doing various operations:
suspend fun operation1(){ //some code }
suspend fun operation2(){ //some code }
suspend fun operation3(){ //some code }
suspend fun operation4(){ //some code }
suspend fun operation5(){ //some code }
I have a function which gets called by an external library (which we can't modify), where I make calls to these functions (of course its logic is more complex in practice). Let's assume the library can make any number of calls in a short time.
fun calledByLibrary(someParam: String){
GlobalScope.launch{
when(someParam){
"someValue1" -> operation1()
"someValue2" -> operation2()
"someValue3" -> operation3()
"someValue4" -> operation4()
"someValue5" -> operation5()
}
}
}
Now, we had a bug, and figured that operation3 and operation4 can not be suspended to run in 'parallel'. They even need to be executed in the same order as called by the external library. For all the other combinations it's ok to do that, so we would like to keep them as suspend to support this.
What's the best way to solve this?
One simple, and easy way would be to use a Mutex in operation3 and operation4 to ensure they don't run in the same time, like this:
val mutex = Mutex()
suspend fun operation3(){ mutex.withLock(){/*some code*/} }
suspend fun operation4(){ mutex.withLock(){/*some code*/} }
But this does not guarantee that they will be executed in the order they have been requested by the library.
Also tried searching for running coroutines in a queue, but surprisingly did not find anything trivial. (Current solution is using a single threaded dispatcher and runBlocking, which is far from ideal)
After tying a lot of things, seems like an actor can be a solution, as a lot of people mentioned using channels, which is a part of the actors. But implementing an actor seemed a complex solution, for such a simple issue, so I tried to avoid it and find a more simple one.
Turned out that the idea with the mutex can be easily fixed to also ensure the order of execution, by using the same thread in the coroutine.
The operation functions already had the logic to use the proper thread when needed (if they did not it would be a trivial thing to add). So the only change needed is to launch the coroutine in the callback on the same thread always. This can be done easily by using Dispachers.Unconfined, or if you want to avoid that, you can create your own single threaded dispacher too.
So, the of course highly simplified solution, would look like this:
fun calledByLibrary(someParam: String){
//You can use any single threaded dispatcher here
//if you want to avoid the unconfined
GlobalScope.launch(Dispachers.Unconfined){
when(someParam){
"someValue1" -> operation1()
"someValue2" -> operation2()
"someValue3" -> operation3()
"someValue4" -> operation4()
"someValue5" -> operation5()
}
}
}
val mutex = Mutex()
suspend fun operation1(){ launch{/*some code*/} }
suspend fun operation2(){ launch{/*some code*/} }
suspend fun operation3(){ mutex.withLock(){launch{/*some code*/}} }
suspend fun operation4(){ mutex.withLock(){launch{/*some code*/}} }
suspend fun operation5(){ launch{/*some code*/} }
You can also do something like this from within a coroutine scope. The await() makes sure to wait for the results before continueing.
val resultOne = async { repository.getSomethingFromApi() }
val resultTwo = async { repository.getSomethingElseFromApi() }
val resultOneFromApi = resultOne.await()
val resultTwoFromApi = resultTwo.await()

How to compose IO functions with other effects in Kotlin Arrow FX

We often need some request validation before handling it. With arrow v 0.8 a typical message handler looked like:
fun addToShoppingCart(request: AddToShoppingCartRequest): IO<Either<ShoppingCardError, ItemAddedEvent>> = fx {
request
.pipe (::validateShoppingCard)
.flatMap { validatedRequest ->
queryShoppingCart().bind().map { validatedRequest to it } // fun queryShoppingCart(): IO<Either<DatabaseError, ShoppingCart>>
}
.flatMap { (validatedRequest, shoppingCart) ->
maybeAddToShoppingCart(shoppingCart, validatedRequest) // fun maybeAddToShoppingCart(...): Either<DomainError, ShoppingCart>
}
.flatMap { updatedShoppingCart ->
storeShoppingCart(updatedShoppingCart).bind() // fun storeShoppingCart(ShoppingCart): IO<Either<DatabaseError, Unit>>
.map {
computeItemAddedEvent(updatedShoppingCart)
}
}
.mapLeft(::computeShoppingCartError)
}
This seems to be a convenient and expressive definition of a workflow. I tried to define similar function in arrow v 0.10.5:
fun handleDownloadRequest(strUrl: String): IO<Either<BadUrl, MyObject>> = IO.fx {
parseUrl(strUrl) // fun(String): Either<BadUrl,Url>
.map {
!effect{ downloadObject(it) } // suspended fun downloadObject(Url): MyObject
}
}
Which results in a compiler error "Suspension functions can be called only within coroutine body". The reason is both map and flatMap functions of Either and Option are not inline.
Indeed, the blog post about fx says
"Soon you will find that you cannot call suspend functions inside the
functions declared for Either such as the ones mentioned above, and
other fan favorites like map() and handleErrorWith(). For that you
need a concurrency library!"
So the question is why is it so and what is the idiomatic way of such composition?
The idiomatic way is
fun handleDownloadRequest(strUrl: String): IO<Either<BadUrl, MyObject>> =
parseUrl(strUrl)
.fold({
IO.just(it.left()) // forward the error
}, {
IO { downloadObject(it) }
.attempt() // get an Either<Throwable, MyObject>
.map { it.mapLeft { /* Throwable to BadURL */ } } // fix the left side
})
Personally I wouldn't go to the deep end of IO with that one, and rewrite as a suspend function instead
suspend fun handleDownloadRequest(strUrl: String): Either<BadUrl, MyObject> =
parseUrl(strUrl)
.fold(::Left) { // forward the error
Either.catch({ /* Throwable to BadURL */ }) { downloadObject(it) }
}
What happened is, in 0.8.X the functions for Either used to be inlined. An unintended side-effect of this was that you could call suspend functions anywhere. While this is nice, it can lead to exceptions thrown (or jumping threads or deadlocks 🙈) in the middle of a map or a flatMap, which is terrible for correctness. It's a crutch.
In 0.9 (or was it 10?) we removed that crutch and made it into something explicit in the API: Either.catch. We kept fold as inlined because it's the same as when, so there was no real correctness tradeoff there.
So, the recommended thing is to use suspend everywhere and only reach for IO when trying to do threading, parallelism, cancellation, retries and scheduling, or anything really advanced.
For basic use cases suspend and Either.catch is enough. To call into a suspend function at the edge of your program or where you need to bridge with these advanced behaviors then use IO.
If you want to continue using Either you can define suspend/inline versions of regular functions at your own risk; or wait until IO<E, A> in 0.11 where you can use effectEither and effectMapEither.

How do I use an async cache with Kotlin coroutines?

I have a Kotlin JVM server application using coroutines and I need to put a cache in front of a non-blocking network call. I figure I can use a Caffeine AsyncLoadingCache to get the non-blocking cache behaviour I need. The AsyncCacheLoader interface I would need to implement uses CompletableFuture. Meanwhile, the method I want to call to load the cache entries is a suspend function.
I can bridge the gap like this:
abstract class SuspendingCacheLoader<K, V>: AsyncCacheLoader<K, V> {
abstract suspend fun load(key: K): V
final override fun asyncLoad(key: K, executor: Executor): CompletableFuture<V> {
return GlobalScope.async(executor.asCoroutineDispatcher()) {
load(key)
}.asCompletableFuture()
}
}
This will run the load function on the provided Executor (by default, the ForkJoinPool), which from the point of view of Caffeine is the correct behaviour.
However, I know that I should try to avoid using GlobalScope to launch coroutines.
I considered having my SuspendingCacheLoader implement CoroutineScope and manage its own coroutine context. But CoroutineScope is intended to be implemented by objects with a managed lifecycle. Neither the cache nor the AsyncCacheLoader has any lifecycle hooks. The cache owns the Executor and the CompletableFuture instances, so it already controls the lifecycle of the loading tasks that way. I can't see that having the tasks be owned by a coroutine context would add anything, and I'm worried that I wouldn't be able to correctly close the coroutine context after the cache stopped being used.
Writing my own asynchronous caching mechanism would be prohibitively difficult, so I'd like to integrate with the Caffeine implementation if I can.
Is using GlobalScope the right approach to implement AsyncCacheLoader, or is there a better solution?
After some thought I've come up with a much simpler solution that I think uses coroutines more idiomatically.
The approach works by using AsyncCache.get(key, mappingFunction), instead of implementing an AsyncCacheLoader. However, it ignores the Executor that the cache is configured to use, following the advice of some of the other answers here.
class SuspendingCache<K, V>(private val asyncCache: AsyncCache<K, V>) {
suspend fun get(key: K): V = supervisorScope {
getAsync(key).await()
}
private fun CoroutineScope.getAsync(key: K) = asyncCache.get(key) { k, _ ->
future {
loadValue(k)
}
}
private suspend fun loadValue(key: K): V = TODO("Load the value")
}
Note that this depends on kotlinx-coroutines-jdk8 for the future coroutine builder and the await() function.
I think ignoring the Executor is probably the right choice. As #Kiskae points out, the cache will use the ForkJoinPool by default. Choosing to use that rather than the default coroutine dispatcher is probably not useful. However, it would be easy to use it if we wanted to, by changing the getAsync function:
private fun CoroutineScope.getAsync(key: K) = asyncCache.get(key) { k, executor ->
future(executor.asCoroutineDispatcher()) {
loadValue(k)
}
}
The cache owns the Executor and the CompletableFuture instances, so it already controls the lifecycle of the loading tasks that way.
This is not true, the documentation on Caffeine specifies that it uses a user-provided Executor or ForkJoinPool.commonPool() if none is provided. This means that there is no default lifecycle.
Regardless directly calling GlobalScope seems like the wrong solution because there is no reason to hardcode a choice. Simply provide a CoroutineScope through the constructor and use GlobalScope as an argument while you don't have an explicit lifecycle for the cache to bind to.
Here's a simple solution. Replace the K, V notation with your type.
val cache = Caffeine.newBuilder().buildAsync<K, V> { key: K, _ ->
val future = CompletableFuture<V>()
launch {
val result = someAwaitOperation(key)
future.complete(result)
}
future
}
Suggest extension method like this
suspend inline fun <K: Any, V: Any> Caffeine<Any, Any>.suspendingLoadingCache(
crossinline suspendedLoader: suspend (key: K) -> V
): AsyncLoadingCache<K, V> =
buildAsync { key, executor: Executor ->
CoroutineScope(executor.asCoroutineDispatcher()).future {
suspendedLoader(key)
}
}
Not recommand GlobalScope, use CoroutineScope(executor.asCoroutineDispatcher())
future method is defined in kotlinx-coroutines-jdk8 module
Here is my solution:
Define an extension function of CoroutineVerticle
fun <K, V> CoroutineVerticle.buildCache(configurator: Caffeine<Any, Any>.() -> Unit = {}, loader: suspend CoroutineScope.(K) -> V) = Caffeine.newBuilder().apply(configurator).buildAsync { key: K, _ ->
// do not use cache's executor
future {
loader(key)
}
}
Create our cache within CoroutineVerticle
val cache : AsyncLoadingCache<String, String> = buildCache({
maximumSize(10_000)
expireAfterWrite(10, TimeUnit.MINUTES)
}) { key ->
// load data and return it
delay(1000)
"data for key: $key"
}
Use the cache
suspend fun doSomething() {
val data = cache.get('key').await()
val future = cache.get('key2')
val data2 = future.await()
}

What's the recommended way to delay Kotlin's buildSequence?

I'm trying to poll a paginated API and provide new items to the user as they appear.
fun connect(): Sequence<T> = buildSequence {
while (true) {
// result is a List<T>
val result = dataSource.getFirstPage()
yieldAll(/* the new data in `result` */)
// Block the thread for a little bit
}
}
Here's the sample usage:
for (item in connect()) {
// do something as each item is made available
}
My first thought was to use the delay function, but I get this message:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope
This is the signature for buildSequence:
public fun <T> buildSequence(builderAction: suspend SequenceBuilder<T>.() -> Unit): Sequence<T>
I think this message means that I can only use the suspend functions in SequenceBuilder: yield and yieldAll and that using arbitrary suspend function calls aren't allowed.
Right now I'm using this to block the sequence building by one second after every time the API is polled:
val resumeTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(1)
while (resumeTime > System.nanoTime()) {
// do nothing
}
This works, but it really doesn't seem like a good solution. Has anybody encountered this issue before?
Why does it not work? Some research
When we look at buildSequence, we can see that it takes an builderAction: suspend SequenceBuilder<T>.() -> Unit as its argument. As a client of that method, you'll be able to hand on a suspend lambda that has SequenceBuilder as its receiver (read about lambda with receiver here).
The SequenceBuilder itself is annotated with RestrictSuspension:
#RestrictsSuspension
#SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> ...
The annotation is defined and commented like this:
/**
* Classes and interfaces marked with this annotation are restricted
* when used as receivers for extension `suspend` functions.
* These `suspend` extensions can only invoke other member or extension
* `suspend` functions on this particular receiver only
* and are restricted from calling arbitrary suspension functions.
*/
#SinceKotlin("1.1") #Target(AnnotationTarget.CLASS) #Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension
As the RestrictSuspension documentation tells, in the case of buildSequence, you can pass a lambda with SequenceBuilder as its receiver but with restricted possibilities since you'll only be able to call "other member or extension suspend functions on this particular receiver". That means, the block passed to buildSequence may call any method defined on SequenceBuilder (like yield, yieldAll). Since, on the other hand, the block is "restricted from calling arbitrary suspension functions", using delay does not work. The resulting compiler error verifies it:
Restricted suspended functions can only invoke member or extension suspending functions on their restricted coroutine scope.
Ultimately, you need to be aware that the buildSequence creates a coroutine that is an example of a synchronous coroutine. In your example, the sequence code will be executed in the same thread that consumes the sequence by calling connect().
How to delay the sequence?
As we learned, The buildSequence creates a synchronous sequence. It's fine to use regular Thread blocking here:
fun connect(): Sequence<T> = buildSequence {
while (true) {
val result = dataSource.getFirstPage()
yieldAll(result)
Thread.sleep(1000)
}
}
But, do you really want an entire thread to be blocked? Alternatively, you can implement asynchronous sequences as described here. As a result, using delay and other suspending functions will be valid.
Just for an alternate solution...
If what you're really trying to do is asynchronously produce elements, you can use Flows which are basically asynchronous sequences.
Here is a quick table:
Sync
Async
Single
Normal valuefun example(): String
suspendingsuspend fun example(): Stringorfun example(): Deferred<String>
Many
Sequencefun example(): Sequence<String>
Flowfun example(): Flow<String>
You can convert your Sequence<T> to a Flow<T> by replacing the sequence { ... } builder with the flow { ... } builder and then replace yield/yieldAll with emit/emitAll:
fun example(): Flow<String> = flow {
(1..5).forEach { getString().let { emit(it) } }
}
suspend fun getString(): String = { ... }
So, for your example:
fun connect(): Flow<T> = flow {
while (true) {
// Call suspend function to get data from dataSource
val result: List<T> = dataSource.getFirstPage()
emitAll(result)
// _Suspend_ for a little bit
delay(1000)
}
}