Is there a better way to write CompletableFutrue.XXXasync() invocations in kotlin? - kotlin

Java CompletableFuture<T> has a lot of async methods, static or instance, in this format
public <U> CompletableFuture<U> XXXasync(SomeFunctionalInterface<T> something, Executor executor)
If you have enough experience with FP in kotlin, you will immediately realize these function are extremely awkward to use in kotlin, because the SAM interface is not the last parameter.
aCompletableFutrue.thenComposeAsync(Function<SomeType, CompletableFuture<SomeOtherType>> {
// ^ WHAT A LONG TYPE NAME THAT NEED TO BE HAND WRITTEN
// do something that has to be written in multiple lines.
// for that sake of simplicity I use convert() to represent this process
convert(it)
}, executor)
That Function has a very very long generic signature that I don't know how to let IDE generate. It will be a plain in the butt if the type name become even longer or contains a ParameterizedType or has type variance annotations.
It also looks nasty because of the trailing , executor) on line 5.
Is there some missing functionality in kotlin or IDE that can help with the situation? At least I don't want to write that long SAM constructor all by myself.
Rejected solutions:
Using named parameter doesn't seem to work because this feature only works on a kotlin function.
Abandon async methods sounds bad from the very beginning.
Kotlin corountine is rejected because we are working with some silly Java libraries that accept CompletionStage only.

IF you calling the api from java that takes a functional interface parameter at last, you can just using lambda in kotlin.
val composed: CompletableFuture<String> = aCompletableFutrue.thenComposeAsync {
CompletableFuture.supplyAsync { it.toString() }
};
Secondly, if you don't like the java api method signature. you can write your own extension methods, for example:
fun <T, U> CompletableFuture<T>.thenComposeAsync(executor: Executor
, mapping: Function1<in T, out CompletionStage<U>>): CompletableFuture<U> {
return thenComposeAsync(Function<T,CompletionStage<U>>{mapping(it)}, executor)
}
THEN you can makes the lambda along the method.
aCompletableFutrue.thenComposeAsync(executor){
// do working
}

Related

Dealing with suspend and non-suspend in Kotlin function parameters

I am fairly new to Kotlin, and am getting to grips with it's implementation of co-routines. I understand that any function that we may want Kotlin to deal with in a non-blocking way needs to be annotated with suspend, and that such functions can only be executed within a co-routine (or within another suspend function). So far so good.
However I keep coming across a problem with utility functions that accept other functions as parameters. For instance with arrow's Try:
suspend fun somethingAsync() = 1 + 1
Try { 1 + 1 } // All is well
Try { somethingAsync() } // Uh oh....
As the parameter to Try's invoke function/operator is not annotated with suspend, the second call will be rejected by the compiler. How does someone deal with this when writing utility functions that can not know if the code inside the passed function or lambda requires suspend or not? Writing a suspend and a non-suspend version of every such function seems incredibly tedious. Have I missed an obvious way to deal with this situation?
First, let's deal with suspend. What it means is this particular function blocks. Not that this function is asynchronous.
Usually, blocking means IO, but not always. In your example, the function doesn't block, nor does it something in an asynchronous manner (hence Async suffix is incorrect there). But lets assume actual utility code does block for some reason.
Now dealing with suspending functions is something that is done on the caller side. Meaning, what would you like to do while this is being executed:
fun doSomething() {
Try { somethingAsync() }
}
If you're fine with doSomething to block, then you can use runBlocking:
fun doSomething() = runBlocking {
Try { somethingAsync() }
}

JacksonSerializer doesn't work in Kotlin Lambda

When I create a client with the JacksonSerializer() feature and make some API calls, then run that script on my local machine, I get no error and the script runs successfully. However, when I upload this script as an AWS Lambda, I get the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of kotlin.coroutines.Continuation, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
At first, I thought the error originated from me constructing the client outside of the Handler class, but when I made the client a private value inside the Handler class, I still get the error. I've included println() statements in my function, but they don't even run. That tells me that my handleRequest() funciton isn't getting run. Is there some AWS/Lambda'ism that prevents me from using the JacksonSerializer() feature as? If so, are there any alternatives on how to parse JSON responses with the Ktor client?
My client construction:
private val client = HttpClient(Apache) {
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
An example call using the client:
val response = client.post<JsonNode> {
url(URL(GITHUB_GRAPHQL_ENDPOINT))
body = reqBody
headers {
append("Authorization", "bearer $token")
}
}
I'm guessing that you made your handler function be a kotlin suspend function? If so, that's your problem.
When you mark a function suspend, the compiler applies a bunch of magic. Most of the time, you don't need to know anything about this, other than the fact that any suspend function gets an extra parameter of type kotlin.coroutines.Continuation added to its signature. You usually don't notice this, since the compiler also makes calls to the function pass along their own hidden Continuation parameter.
Continuation, by design, can't be created by a tool like Jackson - it's an internal thing. What you probably need to do (assuming that you did make your handler function suspend) is to wrap your method in runBlocking {} and make it not be a suspend function. It's probably easiest to create a new handler, like so:
fun fixedHandler(input: MyInput, context: Context) = runBlocking {
originalHandler(input, context)
}
suspend fun originalHandler(input: MyInput, context: Context): MyOutput {
TODO("This is your original code")
}
PS - I've usually found it best to leverage the pre-defined Lambda interfaces to write my Lambda functions - it prevents you from encountering issues like this. See https://docs.aws.amazon.com/lambda/latest/dg/java-handler-using-predefined-interfaces.html for how to do it.
Have you checked out your dependencies running locally versus your dependencies in AWS? I've had issues where locally I'm running with a version, but the version in AWS was different. That could especially explain the error about continuations... Perhaps the method signature is different in whatever version you're using?
Look especially for provided scopes in your gradle/maven pom. Those are easy places for version to be out of sync.

Migrate Java Option call to kotlin

I'm taking my first steps with kotlin.
I am migrating some my existing java code to kotlin.
I have the folllowing line:
storyDate.ifPresent(article::setPublishDate);
Where storyDate is an Optional and article has a method setPublishDate(Date) method.
How would I migrate this line to kotlin?
The auto migrator at https://try.kotlinlang.org is
storyDate.ifPresent(Consumer<Date>({ article.setPublishDate() }))
But this line doesn't compile with the kotlin compiler.
I strongly prefer using extension functions and extension fields, so I've written smth like
val <T> Optional<T>.value: T?
get() = orElse(null)
And then you can use it anywhere (after import) like
myOptional.value?.let {
// handle here
}
It’s rather uncommon to use Optional in Kotlin. If you can make storyDate work as an ordinary unwrapped type, such constructs can often be expressed with a simple let call:
storyDate?.let {
article.setPublishDate(it)
//probably property access works as well:
article.publishDate = it
}
How it works: The safe call ?. will invoke let only if storyDate is not null, otherwise the whole expression evaluates to, again, null. When the variable is not null, let is called with a simple lambda where storyDate is accessible by it (or you can rename it to whatever you like).
Side note:
If storyDate really must be Optional, you can still use the depicted construct by unwrapping it like this:
storyDate.orElse(null)?.let {}
storyDate.ifPresent { Article.setPublishDate(it) }
or
storyDate.ifPresent(Article::setPublishDate)
will work.
In the first example, it denotes the value in the optional instance, which is the Date in the optional storyDate.
I assumed that Article is a class, which has the setPublishDate(Date) static method, because class names are always capitalized.
But if article is an instance, not a class, and it has non-static method, then the following will work.
// article = Article(/* some Article-typed object */)
storyDate.ifPresent { article.setPublishDate(it) }
it has the same meaning as the above one, i.e., the actual Date value in Optional.

Why is it impossible to use method references to `suspend` functions in Kotlin?

I have a list of Job instances which I want to cancel at some point after launch. This looks as follows:
val jobs = arrayListOf<Job>()
//launch and add jobs...
jobs.forEach { it.cancelAndJoin() } // cancels the jobs and waits for completion
Unfortunately, it's not possible to use a method reference here. The reason: cancelAndJoin is a suspend function, as the compiler complains:
jobs.forEach (Job::cancelAndJoin)
"Error:(30, 24) Kotlin: Unsupported [Callable references to suspend functions]"
Why doesn't this work?
UPD: This has already been implemented in Kotlin 1.3.x. Taking a callable reference to a suspending function gives you an instance of KSuspendFunctionN (N = 0, 1, ...). This type has its invoke operator defined as a suspending function, so that you can invoke such a callable reference suspending a coroutine in the same way as a direct invocation would.
Basically, supporting this requires an additional portion of language design and does not simply come bundled with coroutines.
Why is it non-trivial? Because when you take a callable reference of an ordinary function e.g. String::reversed, you get something like a KFunction1<String, String>. If you could do the same with a suspend function, what would you expect to get?
If it's the same KFunctionN<...>, then there's an obvious problem that you can pass it around where an ordinary function is expected and call it, violating the rule that suspend functions can only be called inside coroutines (where the compiler transforms their call sites).
So, it should be something more specific. (I'm currently only speculating, without any idea of actual design attempts) It could be, for example, a SuspendKFunctionN<...>, with its invoke(...) being a suspending function, or it could (less likely) be a special notation only for passing a function reference where a suspend (T) -> R is expected, but anyway, a feature like this requires thorough design to be future-proof.
These helpers currently lack in Kotlin Standard library, but you can implement your own.
For example:
suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit {
val list = this.map { e ->
async(...) {
action(e)
}
}
list.forEach { it.await() }
}
However, what context to pass to async now depends on the threading model your service is using (i.e. do you want to do multi-threading or want to keep everything in a single thread).

Jinq in Kotlin - how to convert lambda into java SerializedLambda?

Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.