Can we have an extension function for suspend function? - kotlin

if i use :
suspend fun <T> A(block: suspend () -> T){
///std.
}
all things go right but :
suspend fun <T> (suspend () -> T).A(){
///std.
}.
no compilation errors , but i cant use it with suspend functions.
for example that we have this fun (do work is a suspend function):
accountManager.doWork(password)
in case #1 its works fine:
A {
accountManager.doWork(password)
}
in case #2 it does not work as expected(compilation error):
accountManager.doWork(password).A()

The receiver of
accountManager.doWork(password).A()
is whatever doWork(password) returns, not the doWork function itself. Let's suppose that doWork returns String, then the above would have worked if A were an extension function on String:
// Depending on what you do in its implementation,
// A does not need to be a suspending function
fun String.A() {
}
If you want A's receiver to be the function doWork instead, the syntax is:
(accountManager::doWork).A()
Then the above will compile if A is declared like this:
suspend fun <T, R> (suspend (T) -> R).A() {
}
Notice that the receiver type is a function that takes one parameter, since doWork takes one parameter.
If what you actually want to do is to use the entire suspending lambda you passed to A as a parameter here...
A {
accountManager.doWork(password)
}
...as the receiver of the new extension function A that you are declaring, then your attempt is correct:
suspend fun <T> (suspend () -> T).A(){
}
You should call it on a suspending lambda:
suspend { accountManager.doWork(password) }.A()
Though I'm not sure why you would prefer this to the way more readable A { ... } syntax.

Related

Suspend function reference as parameter of let gets error

Why Kotlin can't reference to suspend function as the parameter of let/also and other functions?
class X
fun a(x: X) {}
suspend fun b(x: X) {}
X().let(::a)
X().let(::b) // Error: Type mismatch
You can only call suspend functions from a coroutine or another suspend function.
And let does not take a suspend function as a parameter.
public inline fun <T, R> T.let(block: (T) -> R): R
So as with any other type, the function declaration has to match. Passing a suspend function to another function that does not accept a suspend function will not work.
It would work when you have a function like:
This is just an example, no real usecase for a suspend function for printing a log!
suspend inline fun log(block: suspend () -> String) {
val message: String = block() // we assume block takes some time to be computed
return println(message) // once its available, we print it
}
You can use the log function like:
suspend fun complexError(): String {
// takes time to compute...
return "message"
}
// usage
suspend fun errorHandling() {
log(::complexError) // pass a reference to complexError()
// or
log() { complexError() }
}

A 'return' expression required in a function with a block body despite inlined lambda which has the return

I'm trying to create an inlined try-catch helper function but running into a compilation error due to the return statement occurring within the inlined lambda. Below is some code demonstrating the same issue
fun isStringEmpty(myString: String): Boolean {
stringOpHelper {
return myString.length == 0
}
}
inline fun <T> stringOpHelper(fn: () -> T) {
println("performing string operation")
fn()
}
This will compile with the desired effect if a return is added after the inlined function call or an exception is thrown, but that code should be unreachable. Eg:
fun isStringEmpty(myString: String): Boolean {
stringOpHelper {
return myString.length == 0
}
TODO("this is actually unreachable")
}
inline fun <T> stringOpHelper(fn: () -> T) {
println("performing string operation")
fn()
}
My expectation was that the compiler would see that stringOpHelper always calls fn() and the fn in isStringEmpty always returns, so the inlined stringOpHelper call always returns.
Is it possible to define the inline helper function in a way that avoids the need for the unreachable exception / return in the calling function? Otherwise, what's the reason for why this isn't possible?
There is mechanism for such purposes called contracts, but this feature is experimental and its usage must be marked with #ExperimentalContracts or #OptIn(ExperimentalContracts::class)
#OptIn(ExperimentalContracts::class)
inline fun <T> stringOpHelper(fn: () -> T) {
contract {
callsInPlace(fn, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
println("performing string operation")
fn()
}

Kotlin - Automatically match overriden function type?

I'm trying to write a function that is essentially a wrapper method around some other functionality, for instance, some logging function.
I've tried several combinations of inline, generic, reified, etc., but nothing seems to work.
My function looks like this:
fun log(note: String, block: () -> Unit): () -> Unit {
print(note)
return block
}
My idea here is to perform some simple operation on the incoming note, and then just return that incoming function to be used as it was originally.
However, I want to do this around overridden functions like so:
override fun onClick(clicked: View) = log("Green Button") {
// here the regular onClick functionality goes
}
Here, I get an error "Return type is () -> Unit, which is not a subtype of overridden". This makes sense enough, as the function signatures do not match.
However, when I do this with other random functions:
fun test() = log("foo") { ... }
fun otherTest(a: String, b: Int) = log("bar") { ... }
I get no errors, and the interpreter somehow seems fine with this. I also tried looking at something like GlobalScope.launch to take that approach, but I couldn't figure it out.
Is what I'm trying to do possible? If not, is there something close?
I think
inline fun log(note: String, block: () -> Unit): Unit {
print(note)
return block()
}
should do what you want. It can be generalized to
inline fun <T> log(note: String, block: () -> T): T {
print(note)
return block()
}
I get no errors, and the interpreter somehow seems fine with this.
Why is that surprising? Those functions just return () -> Unit. If you do e.g.
fun test() = log("foo") { print("bar") }
then calling test() won't print bar; calling test()() will.
Tell me if my understanding is wrong. This is my approach
Extension function:
fun View.onClickWithLog(str: String, l: () -> Unit) {
setOnClickListener { Log.d("LogTag", str); run(l) }
}
Usage (from Activity):
btnTest.onClickWithLog("My Button String"){
Log.d("Actions from Activity", "Content")
finish()
}
and the output is
D/LogTag: My Button String
D/Actions from Activity: Content
which prints your note first, and execute the actions in the lambda expression.
When you use the = operator to assign something to a fun, the expression on the right hand side is supposed to return the return type of that fun
The original fun onClick(clicked:View) : Unit has return type Unit. When you write
override fun onClick(clicked:View) = ... , the ... is what you get when you call onClick(v) so it should be a Unit instead of a View -> Unit (Not even () -> Unit as in your code)
Take a simpler example. Let say you have fun sum(a:Int,b:Int) : Int. When you write override fun sum(a:Int,b:Int) = ... , ... must be an Int instead of a (Int,Int) -> Int since you expect to get an Int immediately when you call sum(a,b). If you somehow got a let say
val someOtherWayToSum : (Int,Int) -> Int = {...}
and want to use it, you can write
override fun sum(a:Int,b:Int) = someOtherWayToSum(a,b)
In your case, you better just do
override fun onClick(clicked:View){
/* some operation (e.g your log)*/
/* the regular onClick functionality */
}
since you are overriding it and implementing its regular functionality right there anyway.

Kotlin - inline functions with multiple parameters

I can define an inline function in Kotlin with:
inline fun func(a: () -> Unit, b: () -> Unit){
a()
b()
}
But how do I call this function?
For a normal inline function with only one parameter, I would use:
func {
doSomething()
}
Is there a similar syntax for functions with more than one inline parameter?
Only the last function parameter is passed outside the call operator.
For example:
fun foo(a: () -> Unit, b: () -> Unit)
foo({ println("hello") }) {
println("world")
}
The other function parameters are just passed in the normal argument list inside the parenthesis, the last can optionally be moved outside of those parenthesis as is done with most calls.
There are several ways to achieve this.
Probably the nicest is to use a bound function reference for the parameters before the last one:
fun foo(){
func(::foo){foo()}
//this also works:
func(::foo, ::foo)
//or place the other call within parentheses in a lambda. (you can only put the last lambda behind the method call.
func( { foo() } ){foo()}
}
inline fun func(a: () -> Unit, b: () -> Unit){
a()
b()
}
If you want to call an objects method just place that objects name in front of the ::
class Bar {
val baz = Baz()
fun foo() {
func(baz::func2, ::foo)
}
fun func(a: () -> Unit, b: () -> Unit) {
a()
b()
}
}
class Baz{
fun func2(){}
}

Can I access the "enclosing" object for an inlined function call in Kotlin?

Idea: Make an inlined convenience function to synchronize a function call on the "inlining" object's intrinsic lock:
public inline fun <T> intrinsicSync(block: () -> T): T {
return synchronized(intrinsicLockOfTheObjectUsingTheFunction) {
block()
}
}
fun someFunction(parameter: SomeClass) {
intrinsicSync(sharedResource.operation(parameter))
}
Is this possible? If not, is there a different but equally convenient way? I concede that
fun someFunction(parameter: SomeClass) {
synchronized(this) {
sharedResource.operation(parameter)
}
}
isn't the biggest hassle in the world, but I'd still like to know if it's possible to do something similar to what I imagined.
The most you can do is give the function a receiver (i.e. make it an extension function):
public inline fun <T> Any.intrinsicSync(crossinline block: () -> T): T {
// ^ within this function, `this` refers to the receiver
return synchronized(this, block)
}
and then in a member function, you can do
this.intrinsicSync { sharedResource.operation() }
and since this is inferred you should be able to shorten this to
intrinsicSync { sharedResource.operation() }
Keep in mind that extension functions cannot shadow member functions, so if this object has another function with the same signature, it won't choose the extension function.
In your case, though, this seems unlikely.