Currently, I have the following in my code
fun use(consumer: (T) -> Unit) {
consumer(this.value)
}
suspend fun useS(consumer: suspend (T) -> Unit) {
consumer(this.value)
}
These are 2 methods that are actually doing the same. However, I was not able to merge them into one, nor to use overloaded method. In some places my consumer argument is a regular function, on other places it is suspend function; I do not have control over that.
Is it possible to have just one method, regardless of my consumer "suspendability"?
EDIT: forgot to mention that this.value is private and hence using inline would not work - still, I am in control of that, so might change the visibility of the value field.
IF the code really is as simple as you've provided, simply using the non-suspend version and make it inline would solve your issues.
By making it inline (and thus inlining the consumer) it allows the inner block to use the calling environment of the caller. This is why all the library helper functions like also can be used in a suspend function and call suspend functions without explicitly being suspend functions themselves.
Related
Is there any difference between these two Kotlin extension functions?
fun Any?.f(o: Any?) = 100
fun <T> T.g(o: T) = 100
Is it possible to rewrite g in such a way that the type of its argument and receiver are forced to be the same?
That is, 10.g(5) and "x".g("y") are OK, but 10.g("y") does not compile.
Edit:
Given this, I guess the answer to my second question is no, uless one adds additional arguments.
I believe this is not possible officially at the time of writing this answer (Kotlin 1.7.20).
However, internally Kotlin compiler supports such case, it allows to change the default behavior and use exact type parameters. This is controlled by the internal #Exact annotation and it is used in many places across the Kotlin stdlib.
With some hacking we can enable this behavior in our own code:
#Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
fun <T> #kotlin.internal.Exact T.g(o: #kotlin.internal.Exact T) = 100
Of course, this is purely a hack and it may stop working in future versions of Kotlin.
Update
Answering your first question on whether there is a difference between using Any and T. Generic functions make the most sense if the type parameter is not only consumed, but also passed somewhere further. For example, if the function returns T or it receives an object that consumes T:
fun main() {
var result = 5.g(7)
}
fun <T> T.g(o: T): T = if (...) this else o
In this case result is of type Int. If we use Any instead of T, result would have to be Any as well.
I want to call some api in the background every X minutes and then process the json file I get
I've lokked into this documentation: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.-timer/schedule.html
I'm new to kotlin (I used java before) and I have no idea how to use those functions, any examples of usage would be helpful.
Right now I have something like this:
Timer("NameOfMyTimer", true).schedule(refreshImages(knownPosts, knownFiles, httpClient), TimeUnit.MINUTES.toMillis(5))
And the result is:
None of the following functions can be called with the arguments supplied:
public open fun schedule(p0: TimerTask!, p1: Date!): Unit defined in java.util.Timer
public open fun schedule(p0: TimerTask!, p1: Long): Unit defined in java.util.Timer
What did I wrong? How should I call those functions?
I thought that I'm supposed to pass my function "refreshImages" to the timer with list of arguments it should be called with...?
I think I just don't get the "function is object" philosophy right.
You're trying to call
.schedule(refreshImages(knownPosts, knownFiles, httpClient), TimeUnit.MINUTES.toMillis(5))
So you're passing as first argument the result of refreshImages(knownPosts, knownFiles, httpClient), and as second argument a number of milliseconds.
And as you can see from the compilation error, the Timer class has two schedule() methods, but both expect a TimerTask as argument. And your refreshImages method doesn't return a TimerTask, so that doesn't compile.
If you want to use one of these two Timer methods, you need to create an instance of TimerTask, and pass that as argument.
My guess is that you would like to pass a function that will be executed after some delay. That's not what you're doing right now. What you're doing is that you execute refreshImages() immediately, and pass its returned value to schedule().
Passing a function is not possible with the native Timer schedule method: it doesn't expect a function, but a TimerTask. But as the Kotlin documentation you linked to shows, it's possible by calling one of the extension functions of the Kotlin standard library.
The signature of the schedule extension function is
inline fun Timer.schedule(
delay: Long,
crossinline action: TimerTask.() -> Unit
): TimerTask
So, as you can see, its first argument is a delay, and its second argument is a function with TimerTaskas receiver. So you can call this extension function using a delay as first argument, and a lambda as second argment:
timer.schedule(TimeUnit.MINUTES.toMillis(5)) {
refreshImages(knownPosts, knownFiles, httpClient)
}
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() }
}
Consider following simple method:
inline fun foo(crossinline bar: () -> Unit) = foo(2) { bar() }
Now I'm trying to test this implementation:
#Test
fun `test foo`() {
val action = mock<() -> Unit>()
foo(action)
verify(action)()
}
Since this test is written in Kotlin as well, the compiler inlines foo and with test coverage enabled the foo() Java method appears as not covered.
Now I'm wondering, how to configure the environment or the test to not inline functions in such cases?
You can't do that. Inline functions are always inlined; many of their features depend on that, and you can't simply decide not to inline them.
The correct fix for this issue is to implement support for Kotlin inline functions in the coverage framework that you're using, so that it would realize that the function was indeed invoked. The .class files generated by the Kotlin compiler contain enough information for this.
The corresponding issue for jacoco is here.
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).