Kotlin "expected no parameters" when attempting to return inline lambda - intellij-idea

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)
}
}

Related

Why is this Kotlin class property not shadowed by a method parameter?

Looking at this code in kotlinx.coroutines, I noticed something strange:
/**
* Returns a flow containing the results of applying the given [transform] function to each value of the original flow.
*/
public inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R> = transform { value ->
return#transform emit(transform(value))
}
In the first line, the transform used is clearly this.transform (defined here). Shouldn't the transform declared in the method parameter have been used instead, as it is in the second line?
To test this, I wrote a small class which tries to mimc this behaviour:
// flow.kt
class Flow(val name: String) {
public fun transform (transform: (Any) -> Unit): Flow {
return Flow("transformed")
}
public fun emit(value: Any) {
// do nothing
}
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
return#transform(emit(transform(value)))
}
}
And I get the kind of warning I was expecting when I run kotlinc flow.kt:
flow.kt:12:54: error: type mismatch: inferred type is Unit but Flow was expected
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
^
flow.kt:12:66: error: cannot infer a type for this parameter. Please specify it explicitly.
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
^
(Kotlin version as returned by kotlinc -version is "kotlinc-jvm 1.6.10 (JRE 17.0.1+1)")
So why is it that the code defined in kotlinx.coroutines works? If I understand Kotlin's name shadowing rules correctly it shouldn't have.
In kotlinx.couroutines, the transform parameter takes an argument of type T. Hence, this.transform is used when transform is called with a (Any) -> Unit argument.
In your example, the transform parameter takes an argument of type Any. A (Any) -> Unit is an Any and hence the parameter is being used instead of this.transform. Replacing Any with a type parameter will make your code compile too.

How to impose generic constraints to the parameters and return values of a function argument of a generic Kotlin function?

I want to define a Kotlin generic function that takes a lambda as a parameter, but I want to restrict the allowed types of lambdas parameters and return types. How do I do this in Kotlin?
In the following example, I expected the constraint: where T: Base, V: (T) -> Unit to mean that V can only be functions whose first parameter implements Base.
However, I see that the compiler ignores the T: Base part of the constraint and will accept any (Any) -> Unit.
interface Base
fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
println("func is $func")
}
class ImplementsBase : Base
class DoesNotImplementBase
fun main() {
val f1: (ImplementsBase) -> Unit = { }
exampleGenericFunction(f1)
val f2: (DoesNotImplementBase) -> Unit = { }
exampleGenericFunction(f2) // expected this to be a compilation error
}
This unexpected behavior was caused by a bug in the Kotlin >=1.4 compiler.
This bug can be tracked here: https://youtrack.jetbrains.com/issue/KT-48935.

Function overload ambiguous with type covariance but not ambiguous with generic

Suppose I have the following code.
open class Parent
class Child: Parent()
fun <T: Parent> foo(obj: T): T = obj
inline fun <T: Parent> foo(block: () -> T): T = foo(block())
fun <T> bar(obj: T): T = obj
inline fun <T> bar(block: () -> T): T = bar(block())
fun main() {
/* Compile error:
Overload resolution ambiguity. All these functions match.
public inline fun <T : Parent> foo (block: () → TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt
public fun <T : Parent> foo (obj: TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt
*/
foo { Child() }
// Works
bar { "something" }
}
The first function call (foo) gives me a compilation error saying the overload resolution is ambiguous. But why is this true since the block is of type () -> T and this isn't a subtype of Parent?
Shouldn't this error occur on the second function call (bar)? Why doesn't it occur?
My guess is that the problem here is with type inference. In each of your examples, you're relying on type inference for two things:
To determine the value of the generic type parameter T, and
To determine the signature of the lambda function.
In the case of foo, the overload resolution depends on knowing the value of the generic parameter T. That's because T is bounded. The actual value of T might affect whether it's valid to call this method or not, depending on whether T turns out to be a subtype of Parent.
The problem is that T needs to be inferred based on the value of the parameter being passed in, and the type of the parameter being passed in needs to be inferred based on some information about which overload has been selected and/or what the value of T is! I suspect this creates a circular dependency that prevents the overload resolution from completing.
You can fix it by providing a value either for the lambda signature or for the generic type parameter:
foo<Parent> { Child() } // works
foo({ Child() } as () -> Parent) // works
Extracting the lambda to a variable fixes it too, because it makes the compiler infer a type for the variable straight away:
val lambda = { Child() } // type is inferred as () -> Child
foo(lambda) // works, because lambda already has an inferred type
The problem doesn't arise with bar because in that case, T is unbounded. No matter what T ends up being, it won't affect whether that particular overload is allowed to be called or not. That means the compiler doesn't need to infer a value for T before performing overload resolution.
Looks like a bug(s) in the compiler (still present in 1.6.0-M1).
Two workarounds here:
Explicitly specify type argument:
foo<Child> { Child() }
Ironically, doing the same with the working part will break it (but the error won't be about ambiguity, the compiler is pretty sure what overload he's gonna call - the wrong one):
/* Compile error:
Type mismatch.
Required: () → String
Found: String
*/
bar<() -> String>({ "something" })
Extract lambda into a separate variable:
val lambda = { Child() }
foo(lambda)
This will fix the broken part as well:
val block = { "something" }
bar<() -> String>(block)

Extension and higher-order functions referencing

I get an overall idea of what is each of these, I wrote piece of code however that I don't quite understand why it works.The thing is callExtensionOnString expects extension function as its parameter, however it doesn't complain if reference to printString is passed.
Is String.() -> Unit just another name for (String) -> Unit type or is it Kotlin compiler that takes some kind of shortcut to fit higher order in extension function?
fun main(args: Array<String>) {
callExtensionOnString(::printString)
}
fun callExtensionOnString(extensionFun: String.() -> Unit) {
"testString".extensionFun()
}
fun printString(str: String) {
println(str)
}
Then there is second thing that seems unclear to me. printString reference is passed once to callExtensionOnString and then again to run function which basically requires String.() -> Unit extension function. Is there difference between these two so that second time :: prefix is omitted?
fun main(args: Array<String>) {
runFunctionOnString(::printString)
}
fun runFunctionOnString(higherOrderFun: (String) -> Unit) {
"testString".run(higherOrderFun)
}
fun printString(str: String) {
println(str)
}
Concerning your first question, these 2 are equivalent:
"testString".extensionFun()
extensionFun("testString")
That's why passing printString method doesn't cause a compilation error.
Concerning the second question, the difference is that highOrderFun is already a reference to a function, while printString is not a function reference (but you can obtain it with the :: operator)

Type name as value?

Can somebody point me to the documentation explaining what the following means? Especially, I'd like to know why String and Int can be used as shown.
val a: Unit = { _: Any -> String }(Int)
Initially, I had written this:
val a: Unit = { x: Any -> x.toString() }(Unit)
In both cases, I've failed to find the right documentation.
Let's break down the first line.
{ _: Any -> String }
This is a lambda. It takes one parameter, that's marked as never used by giving it the name _. The parameter of this type is specified as Any. The lambda returns String, which refers to the (completely empty) companion object inside the String class. The type of this lambda is therefore (Any) -> String.Companion.
{ _: Any -> String }(Int)
This lambda is invoked by the parentheses after it, passing in Int, which again refers to the companion object inside that class (this one isn't empty, it contains constants). This works, because of course, Int.Companion is a subtype of Any, the expected parameter type.
val a: Unit = { _: Any -> String }(Int)
Finally, the declaration of a with its explicit Unit type forces the lambda to change its inferred type from (Any) -> String.Companion to (Any) -> Unit, so it can return the expected explicit type after it's invoked.
The second line is more of the same, only simpler without the companion objects.
Type name as value?
String and Int are the companion objects (sort of the equivalent to Java's static) of the String and Int classes. The name of a class is synonymous with the name of its companion object, i.e. Int more or less is the same as Int.Companion, etc.
{ _:Any -> String } and { x:Any -> x.toString() } are both lambdas of type (Any) -> String.Companion and (Any) -> String, but since the result of the function call is assigned to a, which is of type Unit, these functions are both inferred to return Unit.
Basically, after type inference:
{ _:Any -> String } is a (Any) -> Unit
{ x:Any -> x.toString() } is also a (Any) -> Unit
This type corresponds to the void type in Java.
Documentation: Unit
So these functions both effectively do nothing.
The first function (of type (Any) -> Unit takes one argument (which is unused) and returns String.Companion.
In this case, it is called with Int.Companion... but the argument is unused, so it doesn't matter.
You could have written:
val lambda: (Any) -> Unit = { _: Any -> String }
val a: Unit = lambda(Int)
The second function calls toString on its parameter, which is in this case Unit. The string representation of Unit is defined as "kotlin.Unit", but since it is assigned to a the function's return type is inferred to be Unit, so the function doesn't return anything useful.
These functions are both quite useless as they return Unit (i.e. void, nothing). What do you want to accomplish?
You defined a lambda { _: Any -> String } of type (Any)-> String.Companion and called it with the Int.Companion object. It always returns the String.Companion object. Does not make much sense. The following might be more readable:
val func = { _: Any -> String.Companion }
val res = func(Int.Companion)
Note that Int and Int.Companion are used interchangeably here (also applies for the String one).
Information about companion objects can be found in the documentation.