What is the difference between crossinline and noinline in Kotlin? - kotlin

This code compiles with a warning (insignificant performance impact):
inline fun test(noinline f: () -> Unit) {
thread(block = f)
}
This code does not compile (illegal usage of inline-parameter):
inline fun test(crossinline f: () -> Unit) {
thread(block = f)
}
This code compiles with a warning (insignificant performance impact):
inline fun test(noinline f: () -> Unit) {
thread { f() }
}
This code compiles with no warning or error:
inline fun test(crossinline f: () -> Unit) {
thread { f() }
}
Here are my questions:
How come (2) does not compile but (4) does?
What exactly is the difference between noinline and crossinline?
If (3) does not generates a no performance improvements, why would (4) do?

From the inline functions reference:
Note that some inline functions may call the lambdas passed to them as parameters not directly from the function body, but from another execution context, such as a local object or a nested function. In such cases, non-local control flow is also not allowed in the lambdas. To indicate that, the lambda parameter needs to be marked with the crossinline modifier
Hence, example 2. doesn't compile, since crossinline enforces only local control flow, and the expression block = f violates that. Example 1 compiles, since noinline doesn't require such behavior (obviously, since it's an ordinary function parameter).
Examples 1 and 3 do not generate any performance improvements, since the only lambda parameter is marked noinline, rendering the inline modifier of the function useless and redundant - the compiler would like to inline something, but everything that could be has been marked not to be inlined.
Consider two functions, A and B
A
inline fun test(noinline f: () -> Unit) {
thread { f() }
}
B
fun test(f: () -> Unit) {
thread { f() }
}
Function A behaves like function B in the sense that the parameter f will not be inlined (the B function doesn't inline the body of test whereas in the A function, the body: thread { f() } still gets inlined).
Now, this is not true in the example 4, since the crossinline f: () -> Unit parameter can be inlined, it just cannot violate the aforementioned non-local control flow rule (like assigning new value to a global variable). And if it can be inlined, the compiler assumes performance improvements and does not warn like in the example 3.

Let me try to explain this by example: I'll go through each of your examples and describe what it orders the compiler to do. First, here's some code that uses your function:
fun main(args: Array<String>) {
test {
println("start")
println("stop")
}
}
Now let's go through your variants. I'll call the functions from your examples test1..test4 and I'll show in pseudocode what the above main function would compile into.
1. noinline, block = f
This code compiles with a warning (insignificant performance impact)
inline fun test1(noinline f: () -> Unit) {
thread(block = f)
}
fun compiledMain1() {
val myBlock = {
println("start")
println("stop")
}
thread(block = myBlock)
}
First, note there's no evidence of inline fun test1 even existing. Inline functions aren't really "called": it's as if the code of test1 was written inside main(). On the other hand, the noinline lambda parameter behaves same as without inlining: you create a lambda object and pass it to the thread function.
2. crossinline, block = f
This code does not compile (illegal usage of inline-parameter)
inline fun test2(crossinline f: () -> Unit) {
thread(block = f)
}
fun compiledMain2() {
thread(block =
println("start")
println("stop")
)
}
I hope I managed to conjure what happens here: you requested to copy-paste the code of the block into a place that expects a value. It's just syntactic garbage. Reason: with or without crossinline you request that the block be copy-pasted into the place where it's used. This modifier just restricts what you can write inside the block (no returns etc.)
3. noinline, { f() }
This code compiles with a warning (insignificant performance impact)
inline fun test3(noinline f: () -> Unit) {
thread { f() }
}
fun compiledMain3() {
val myBlock = {
println("start")
println("stop")
}
thread { myBlock() }
}
We're back to noinline here so things are straightforward again. You create a regular lambda object myBlock, then you create another regular lambda object that delegates to it: { myBlock() }, then you pass this to thread().
4. crossinline, { f() }
This code compiles with no warning or error
inline fun test4(crossinline f: () -> Unit) {
thread { f() }
}
fun compiledMain4() {
thread {
println("start")
println("stop")
}
}
Finally this example demonstrates what crossinline is for. The code of test4 is inlined into main, the code of the block is inlined into the place where it's used. But, since it's used inside the definition of a regular lambda object, it can't contain non-local control flow.
About the Performance Impact
The Kotlin team wants you to use the inlining feature sensibly. With inlining the size of the compiled code can explode dramatically and even hit the JVM limits of up to 64K bytecode instructions per method. The main use case is higher-order functions that avoid the cost of creating an actual lambda object, only to discard it right after a single function call which happens right away.
Whenever you declare an inline fun without any inline lambdas, inlining itself has lost its purpose. The compiler warns you about it.

Q1: How come (2) does not compile but (4) does?
From their doc:
Inlinable lambdas can only be called inside the inline functions or passed as inlinable arguments...
Answer:
The method thread(...) is not an inline method so you won't be able to pass f as an argument.
Q2: What exactly is the difference between noinline and crossinline?
Answer:
noinline will prevent the inlining of lambdas. This becomes useful when you have multiple lambda arguments and you want only some of the lambdas passed to an inline function to be inlined.
crossinline is used to mark lambdas that mustn't allow non-local returns, especially when such lambda is passed to another execution context. In other words, you won't be able to do a use a return in such lambdas. Using your example:
inline fun test(crossinline f: () -> Unit) {
thread { f() }
}
//another method in the class
fun foo() {
test{
//Error! return is not allowed here.
return
}
}
Q3: If (3) does not generates a no performance improvements, why would (4) do?
Answer:
That is because the only lambda you have in (3) has been marked with noinline which means you'll have the overhead cost of creating the Function object to house the body of your lamda. For (4) the lambda is still inlined (performance improvement) only that it won't allow non-local returns.

To the first and second question
How come (2) does not compile but (4) does?.. difference between noinline and crossinline
2. inline fun test(crossinline f: () -> Unit) {
thread(block = f)
}
4. inline fun test(crossinline f: () -> Unit) {
thread { f() }
}
Both cases have inline modifier instructing to inline both the function test and its argument lambda f. From kotlin reference:
The inline modifier affects both the function itself and the lambdas
passed to it: all of those will be inlined into the call site.
So the compiler is instructed to place the code (inline) instead of constructing and invoking a function object for f. crossinline modifier is only for inlined things: it just says that the passed lambda (in f parameter) should not have non-local returns (which "normal" inlined lambdas may have). crossinline can be thought of as something like this (instruction to the compiler ): “ do inline but there is a restriction that it is crossing the invoker context and so make sure the lambda does not have non-local returns.
On a side note, thread seems like a conceptually illustrative example for crossinline because obviously returning from some code (passed in f) later on a different thread cannot possibly affect the return from test, which continues to execute on the caller thread independently from what it spawned (f goes on to execute independently)..
In case #4, there is a lambda (curly braces) invoking f(). In case #2, f is passed directly as an argument to thread
So in #4, call f() can be inlined and the compiler can guarantee there is no non-local return. To elaborate, the compiler would replace f() with its definition and that code is then “wrapped” inside the enclosing lambda, in other words, { //code for f() } is sort of another (wrapper) lambda and it itself is further passed as a function object reference (to thread).
In case #2, the compiler error simply says it cannot inline f because it is being passed as a reference into an “unknown” (non-inlined) place. crossinline becomes out of place and irrelevant in this case because it could be applied only if f were inlined.
To sum up, case 2 and 4 are not the same by comparing to the example from the kotlin reference (see "Higher-Order Functions and Lambdas"): below invocations are equivalent, where curly braces (lambda expression) "replace" the wrapper function toBeSynchronized
//want to pass `sharedResource.operation()` to lock body
fun <T> lock(lock: Lock, body: () -> T): T {...}
//pass a function
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
//or pass a lambda expression
val result = lock(lock, { sharedResource.operation() })
Case #2 and #4 in the question are not equivalent because there is no "wrapper" invoking f in #2

Related

Are Kotlin scope function blocks effectively inline?

I'm writing a Kotlin inline class to make Decimal4J more convenient without instantiating any objects. I'm worried that scope functions might create lambda objects, thereby making the whole thing pointless.
Consider the function compareTo in the following example.
/* imports and whatnot */
#JvmInline
value class Quantity(val basis: Long) {
companion object {
val scale: Int = 12
val metrics: ScaleMetrics = Scales.getScaleMetrics(scale)
val arithmetic: DecimalArithmetic = metrics.defaultArithmetic
}
operator fun compareTo(alt: Number): Int {
with(arithmetic) {
val normal = when (alt) {
is Double -> fromDouble(alt)
is Float -> fromFloat(alt)
is Long -> fromLong(alt)
is BigDecimal -> fromBigDecimal(alt)
is BigInteger -> fromBigInteger(alt)
else -> fromLong(alt.toLong())
}
return compare(basis, normal)
}
}
}
Does the with(arithmetic) scope create a lambda in the heap? The docs on kotlinlang.org consistently refer to the scoped code as a lambda expression. Is there any way to use scope functions without creating objects?
All of the built-in scoping functions, including with, are marked inline, which means the implementation gets planted directly in the code that's calling it. Once that happens, the lambda call can be optimized away.
To be more concrete, here's the implementation of with (with the Kotlin contracts stuff removed, since that's not relevant here)
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
Extension methods are, and always have been, syntax sugar resolved at compile time, so this is effectively
public inline fun <T, R> with(receiver: T, block: (T) -> R): R {
return block(receiver) // (with `this` renamed by the compiler)
}
So when we call
operator fun compareTo(alt: Number): Int {
with (arithmetic) {
println("Hi :)")
println(foobar()) // Assuming foobar is a method on arithmetic
}
}
The inline will transform this into
operator fun compareTo(alt: Number): Int {
({
println("Hi :)")
println(it.foobar()) // Assuming foobar is a method on arithmetic
})(arithmetic)
}
And any optimizer worth its salt can see that this is a function that's immediately evaluated, so we should go ahead and do that now. What we end up with is
operator fun compareTo(alt: Number): Int {
println("Hi :)")
println(arithmetic.foobar()) // Assuming foobar is a method on arithmetic
}
which is what you would have written to begin with.
So, tl;dr, the compiler is smart enough to figure it out. You don't have to worry about it. It's one of the perks of working in a high-level language.
By the way, this isn't just abstract. I just compiled the above code on my own machine and then decompiled the JVM bytecode to see what it really did. It was quite a bit noisier (since the JVM, by necessity, has a lot of noise), but there was no lambda object allocated, and the function was just one straight shot that calls println twice.
In case you're interested, Kotlin takes this example function
fun compareTo(alt: Number): Unit {
return with(arithmetic) {
println("Hi :)")
println(foobar())
}
}
to this Java, after being decompiled,
public static final void compareTo-impl(long arg0, #NotNull Number alt) {
Intrinsics.checkNotNullParameter((Object)alt, (String)"alt");
long l = arithmetic;
boolean bl = false;
boolean bl2 = false;
long $this$compareTo_impl_u24lambda_u2d0 = l;
boolean bl3 = false;
String string = "Hi :)";
boolean bl4 = false;
System.out.println((Object)string);
int n = so_quant.foobar-impl($this$compareTo_impl_u24lambda_u2d0);
bl4 = false;
System.out.println(n);
}
Quite a bit noisier, but the idea is exactly the same. And all of those pointless local variables will be taken care of by a good JIT engine.
Just some additional info to help clear up the terminology that led to your confusion.
The word “lambda” is defined as a syntax for writing a function. The word does not describe a function itself, so the word lambda has nothing to do with whether a function object is being allocated or not.
In Kotlin, there are multiple different syntaxes you can choose from to define or refer to a function. Lambda is only one of these.
// lambda assigned to variable
val x: (String) -> Unit = {
println(it)
}
// anonymous function assigned to variable
val y: (String) -> Unit = fun(input: String) {
println(input)
}
// reference to existing named function assigned to variable
val z: (String) -> Unit = ::println
// lambda passed to higher order function
“Hello World”.let { println(it) }
// anonymous function passed to higher order function
“Hello World”.let(fun(input: Any) { println(input) })
// reference to existing named function passed to higher order function
“Hello World”.let(::println)
// existing functional reference passed to higher order function
“Hello World”.let(x)
There is actually no such thing as a lambda object that can be passed around. The object is a function that could have been defined using any of the above syntaxes. Once a functional reference exists, the syntax that was used to create it is irrelevant.
With inline higher order functions, as the standard library scope functions are, the compiler optimizes away the creation of the functional object altogether. Of the four higher order calls in my example above, the first three will compile to the same thing. The last is a bit different because the function x already exists so it will be x itself that is invoked in the inlined code. Its contents don’t get hoisted out and called directly in the inlined code.
The advantage of using lambda syntax for higher order inline function calls is that it enables you to use keywords for the outer scope (non-local returns), such as return, continue, or break.

Why we need noinline in kotlin?

I'm reading kotlin doc and can't understend one thing.
We can use noinline modifier to mark that lambda shouldn't be inline. But what is the real case when we really need it?
I understend what noinline does, but can't understand why.
Notice that when inline is added to a function/method declaration, all of its lambda parameters are inlined.
Also notice that there are some ways in which you can use a lambda parameter, that requires the lambda to be not inlined. For example, passing it to another, non-inlined function, or assigning it to a variable:
var func: (() -> Unit)? = null
inline fun foo(x: () -> Unit, y: () -> Unit) {
notInlined(x) // x must be not inlined for this to work
func = x // x must be not inlined for this to work
}
So what if I want to do notInlined(x), but I still want to inline y? This is where I use noinline:
inline fun foo(noinline x: () -> Unit, y: () -> Unit)
IntelliJ even gives this as a suggestion:
One thing I notice is that this for example is allowed, but "end of runLambda" is never reached then.
fun main() {
runLambda {
println("lambda")
return
}
}
inline fun runLambda(lambda: ()->Unit) {
lambda.invoke()
println("end of runLambda")
}
if you want to ensure that the function completely runs until the end noinline could fix it because then it won't allow the return
Although crossinline can do the same and I think that one is actually meant for that. I'm not sure what the difference is

how to pass suspend function as explicit parameter to coroutine builder?

I'm looking into launch coroutine builder which takes coroutine code as block: suspend CoroutineScope.() -> Unit. We usually pass the code as lambda. However, I was wondering how to pass this function as explicit parameter to launch function.
coroutineScope {
launch(block = ::myFunction)
}
suspend fun CoroutineScope.myFunction(): Unit {
// coroutine code
}
It gives following error
Type mismatch.
Required:
suspend CoroutineScope.() → Unit
Found:
KSuspendFunction0<Unit>
What is it that i'm missing?
The syntax for extension function references is the same as for member functions:
launch(block = CoroutineScope::myFunction)
How about this way?
coroutineScope {
launch(block = myFunction())
}
fun myFunction(): suspend CoroutineScope.() -> Unit = {
for(i in 3 downTo 1) {
println("$i")
delay(1000)
}
}
According to kotlin doc, launch function with parameter is function type: CoroutineScope.() → Unit, is one function type with receiver.
Function types with receiver, such as A.(B) -> C, can be instantiated with a special form of function literals – function literals with receiver.
The same article also noted the following:
Using a callable reference to an existing declaration:
a top-level, local, member, or extension function: ::isOdd, String::toInt,
a top-level, member, or extension property: List<Int>::size,
a constructor: ::Regex
These include bound callable references that point to a member of a particular instance: foo::toString.
but not adaptive to "function literals with receiver".
so one way to make it work:
coroutineScope {
launch {
myFunction()
}
}

Kotlin "expected no parameters" when attempting to return inline lambda

I'm trying to write a Kotlin function which returns a lambda taking a parameter. I'm attempting to use code like the following to do this:
fun <T> makeFunc() : (T.() -> Unit) {
return { t: T ->
print("Foo")
}
}
Note: In the actual program, the function is more complex and uses t.
Kotlin rejects this as invalid, giving an 'Expected no parameters' error at t: T.
However, assigning this lambda to a variable first is not rejected and works fine:
fun <T> makeFunc() : (T.() -> Unit) {
val x = { t: T ->
print("Foo")
}
return x
}
These two snippets seem identical, so why is this the case? Are curly braces after a return statement interpreted as something other than a lambda?
Additionally, IntelliJ tells me that the variable's value can be inlined, whereas this appears to cause the error.
There is a curious moment in the design of functional types and lambda expressions in Kotlin.
In fact, the behavior can be described in these two statements:
Named values of functional types are interchangeable between the ordinary functional type like (A, B) -> C and the corresponding type of function with the first parameter turned into receiver A.(B) -> C. These types are assignable from each other.
So, when you declare a variable that is typed as (T) -> Unit, you can pass it or use it where T.() -> Unit is expected, and vice versa.
Lambda expressions, however, cannot be used in such free manner.
When a function with receiver T.() -> Unit is expected, you cannot place a lambda with a parameter of T in that position, the lambda should exactly match the signature, a receiver and the first parameter cannot be converted into each other:
Shape of a function literal argument or a function expression must exactly match the extension-ness of the corresponding parameter. You can't pass an extension function literal or an extension function expression where a function is expected and vice versa. If you really want to do that, change the shape, assign literal to a variable or use the as operator.
(from the document linked above)
This rule makes lambdas easier to read: they always match the expected type. For instance, there's no ambiguity between a lambda with receiver and a lambda with implicit it that is simply unused.
Compare:
fun foo(bar: (A) -> B) = Unit
fun baz(qux: A.() -> B) = Unit
val f: (A) -> B = { TODO() }
val g: A.() -> B = { TODO() }
foo(f) // OK
foo(g) // OK
baz(f) // OK
baz(g) // OK
// But:
foo { a: A -> println(a); TODO() } // OK
foo { println(this#foo); TODO() } // Error
baz { println(this#baz); TODO() } // OK
baz { a: A -> println(a); TODO() } // Error
Basically, it's the IDE diagnostic that is wrong here. Please report it as a bug to the Kotlin issue tracker.
You are defining a function type () -> Unit on receiver T, there really isn't a parameter to that function, see "()". The error makes sense. Since you define the function type with T as its receiver, you can refer to T by this:
fun <T> makeFunc(): (T.() -> Unit) {
return {
print(this)
}
}

Kotlin's crossinline keyword

I have read this question but I have a more fundamental question regarding the crossinline keyword. I'm not really sure what problem it is solving and how it solves it.
From the Kotlin Docs,
Note that some inline functions may call the lambdas passed to them as parameters not directly from the function body, but from another execution context, such as a local object or a nested function. In such cases, non-local control flow is also not allowed in the lambdas. To indicate that, the lambda parameter needs to be marked with the crossinline modifier:
[Emphasis added]
This statement is ambiguous to me. First, I am having trouble actually picturing what is meant by "such cases". I have a general idea of what the issue is but can't come up with a good example of it.
Second, the phrase "To indicate that," can be read multiple ways. To indicate what? That a particular case is not allowed? That it is allowed? That non-local control flow in a given function definition is (or is not) allowed?
In short, I have trouble figuring out what the context for using this really is, what using it communicates to clients, and what the expected results of applying this keyword are.
First, I am having trouble actually picturing what is meant by "such cases". I have a general idea of what the issue is but can't come up with a good example of it.
Here's an example:
interface SomeInterface {
fun someFunction(): Unit
}
inline fun someInterfaceBy(f: () -> Unit): SomeInterface {
return object : SomeInterface {
override fun someFunction() = f()
// ^^^
// Error: Can't inline 'f' here: it may contain non-local returns.
// Add 'crossinline' modifier to parameter declaration 'f'.
}
}
Here, the function that is passed to someInterfaceBy { ... } is inlined inside an anonymous class implementing SomeInterface. Compilation of each call-site of someInterfaceBy produces a new class with a different implementation of someFunction().
To see what could go wrong, consider a call of someInterfaceBy { ... }:
fun foo() {
val i = someInterfaceBy { return }
// do something with `i`
}
Inside the inline lambda, return is non-local and actually means return from foo. But since the lambda is not called and leaks into the object i, return from foo may be absolutely meaningless: what if i.someFunction() (and thus the lambda) is called after foo has already returned or even in a different thread?
Generically, 'such cases' means inline functions that call their functional parameters not in their own bodies (effectively, i.e. taking other inline functions into account) but inside some other functions they declare, like in non-inline lambdas and anonymous objects.
Second, the phrase "To indicate that," can be read multiple ways. To indicate what? That a particular case is not allowed? That it is allowed? That non-local control flow in a given function definition is (or is not) allowed?
This is exactly how the problem I described above is fixed in the Kotlin language design: whenever an inline function intends to inline its functional parameter somewhere where it could be not called in-place but stored and called later, the parameter of the inline function should be marked as crossinline, indicating that non-local control flow is not allowed in the lambdas passed here.
Problem: non-local return
Let's first understand the problem of non-local return with a simple example:
fun doSomething() {
println("Before lambda")
doSomethingElse {
println("Inside lambda")
return // This is non-local return
}
println("After lambda")
}
inline fun doSomethingElse(lambda: () -> Unit) {
println("Do something else")
lambda()
}
Non-local return
In the code above, the return statement is called a non-local return because it's not local to the function in which it is called. This means this return statement is local to the doSomething() function and not to the lambda function in which it is called. So, it terminates the current function as well as the outermost function.
Local return
If you just wanted to return from the lambda, you would say return#doSomethingElse. This is called local return and it is local to the function where it is specified.
Problem
Now the problem here is that the compiler skips the lines after the non-local return statement. The decompiled bytecode for the doSomething() looks like following:
public static final void doSomething() {
System.out.println("Before lambda");
System.out.println("Doing something else");
System.out.println("Inside lambda");
}
Notice that there is no statement generated for the line println("After lambda"). This is because we have the non-local return inside the lambda and the compiler thinks the code after the return statement is meaningless.
Solution: crossinline keyword
crossinline
In such cases (like the problem mentioned above), the solution is to disallow the non-local return inside the lambda. To achieve this, we mark the lambda as crossinline:
inline fun doSomethingElse(crossinline lambda: () -> Unit) {
println("Doing something else")
lambda()
}
Non-local return disallowed
When you use the crossinline keyword, you are telling the compiler, "give me an error, if I accidentally use a non-local return inside the nested functions or local objects.":
fun doSomething() {
println("Before lambda")
doSomethingElse {
println("Inside lambda")
return // Error: non-local return
return#doSomethingElse // OK: local return
}
println("After lambda")
}
Now the compiler generates the bytecode as expected:
public static final void doSomething() {
System.out.println("Before lambda");
System.out.println("Doing something else");
System.out.println("Inside lambda");
System.out.println("After lambda");
}
That's it! Hope I made it easier to understand.