In the following example (also on Kotlin Playground), IntelliJ offers to change the lambda (as in the commented-out first line of main) to a method reference (as shown in the second line of main).
fun Int.matches1(n: Int): Boolean = TODO()
fun Int.matches2(n: Int): Boolean = TODO()
fun main() {
// val matches: Int.(Int) -> Boolean = { n -> matches1(n) }
val matches: Int.(Int) -> Boolean = ::matches1
}
The line containing the lambda works fine. However, if I try to use the method reference, the Kotlin compiler rejects it with Unresolved reference: matches1. Why?
You need to specify the receiver type, since you're trying to reference an extension function. It's similar to when you want to reference a function that belongs to a class. So this works:
val matches: Int.(Int) -> Boolean = Int::matches1
Related
I'm having trouble understanding lambda.
In particular, things like T.() -> R in the run() is more confusing.
public inline fun <T, R> T.run(block: T.() -> R): R = return block()
Questions
fun main() {
test {
this.replace("3","B")
}
}
fun test(block: String.(Int) -> Unit) {
"TEST".block(7)
}
In this code, in the block parameter, String.() means to define block as an extension function of the String class? So block requires String value when calling?
and,
fun main() {
"A".test {
this.replace("3","B")
}
}
fun String.test(block: String.(Int) -> Unit) {
block(7)
}
When calling the block in this code, why don't we need the receiver type?
in the block parameter, String.() means to define block as an extension function of the String class? So block requires String value when calling?
Yes. The block is effectively an extension function on the String class: within the block, this is the relevant String instance.
When calling the block in this code, why don't we need the receiver type?
Because there's already an implicit receiver. test() is itself an extension function on String, so within the body of test(), this is a String. And so you can call any of the methods of String without needing to qualify them with this. — including extension methods such as block.
Consider a simpler case with ‘real’ methods instead of extension methods:
class C {
fun a() {
// …
}
fun b() {
a() // Implicit receiver
}
}
a() is a method of class C, and so it needs a instance of C. But b() doesn't need to specify an instance of C when calling a(), because it already has its own receiver.
It could have been written as this.a(), but there's no need, as this is always an implied receiver when you don't specify one. (Some people seem to like an explicit this., but to me it's just pointless visual noise…)
Although extension methods are implemented a little differently ‘under the covers’, the syntax works in exactly the same way. So the call to block() in your second example has an implicit this..
In particular, things like T.() -> R in the run() is more confusing.
public inline fun <T, R> T.run(block: T.() -> R): R = block()
Here T and R are of a generic type, which could be of any data type Int, String or a Class etc. You get the picture.
block: T.() -> R, it's saying that the block has to be an extension function of type T, that could return any type R and this R could be anything, Unit, String, Class etc.
A lambda returns the value of its last expression, so whatever expression is on the last line of your lambda it would return it, that is of type R.
Now when we use that run method on any object, it gives us the same object as the lambda receiver (this) inside the lambda, because our lambda is an extension function of the same type on which we've called this.
var a = 1
val b = a.run {
this + 6
"incremented"
}
On using run method on a, our generic T type becomes an Int and our object a is available inside the lambda as this, because, now it's an extension function of Int.
In lambda our last expression is "incremented" which is a String so here our R becomes a type of String. As the run method is returning this R, the value of variable b becomes incremented.
In this code, in the block parameter, String.() means to define block
as an extension function of the String class? So block requires String
value when calling?
If block is an extension function then you won't need to pass a String. When you call it on any String it would use that. But if block is not an extension function then you would've to pass it.
fun test(block: String.(Int) -> Unit) {
"TEST".block(7)
}
.
fun String.test(block: String.(Int) -> Unit) {
block(7)
}
When calling the block in this code, why don't we need the receiver type?
In latter, the test method is an extension function of String, which is available as this in the method body, and all of the functions of the String are also available, that you can use with or without this on the receiver object. As block is also an extension function of String it can be accessed directly.
Meanwhile, in the former, there is no receiver object of type String is available in the method body, which is why you've to explicitly call it on a String.
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.
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)
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)
}
}
In an attempt to understand more about Kotlin and play around with it, I'm developing a sample Android app where I can try different things.
However, even after searching on the topic for a while, I haven't been able to find a proper answer for the following issue :
Let's declare a (dummy) extension function on View class :
fun View.isViewVisibility(v: Int): Boolean = visibility == v
Now how can I reference this function from somewhere else to later call invoke() on it?
val f: (Int) -> Boolean = View::isViewVisibility
Currently gives me :
Error:(57, 35) Type mismatch: inferred type is KFunction2 but (Int) -> Boolean was
expectedError:(57, 41) 'isViewVisibility' is a member and an extension
at the same time. References to such elements are not allowed
Is there any workaround?
Thanks !
Extensions are resolved statically, where the first parameter accepts an instance of the receiver type. isViewVisibility actually accept two parameters, View and Int. So, the correct type of it should be (View, Int) -> Boolean, like this:
val f: (View, Int) -> Boolean = View::isViewVisibility
The error message states:
'isViewVisibility' is a member and an extension at the same time. References to such elements are not allowed
It's saying that the method is both an extension function, which is what you're wanting it to be, and a member. You don't show the entire context of your definition, but it probably looks something like this:
// MyClass.kt
class MyClass {
fun String.coolStringExtension() = "Cool $this"
val bar = String::coolStringExtension
}
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
As you can see the coolStringExtension is defined as a member of MyClass. This is what the error is referring to. Kotlin doesn't allow you to refer to extension function that is also a member, hence the error.
You can resolve this by defining the extension function at the top level, rather than as a member. For example:
// MyClass.kt
class MyClass {
val bar = String::coolStringExtension
}
fun String.coolStringExtension() = "Cool $this"
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
A better fit is the extension function type View.(Int) -> Boolean:
val f: View.(Int) -> Boolean = View::isViewVisibility
But actually the extension types are mostly interchangeable (assignment-compatible) with normal function types with the receiver being the first parameter:
View.(Int) -> Boolean ↔ (View, Int) -> Boolean
I faced the same problem when I declared extension function inside another class and try to pass that extension function as parameter.
I found a workaround by passing function with same signature as extension which in turn delegates to actual extension function.
MyUtils.kt:
object MyUtils {
//extension to MyClass, signature: (Int)->Unit
fun MyClass.extend(val:Int) {
}
}
AnyClass.kt:
//importing extension from MyUtils
import MyUtils.extend
// Assume you want to pass your extension function as parameter
fun someMethodWithLambda(func: (Int)->Unit) {}
class AnyClass {
fun someMethod() {
//this line throws error
someMethodWithLambda(MyClass::extend) //member and extension at the same time
//workaround
val myClassInstance = MyClass()
// you pass a proxy lambda which will call your extension function
someMethodWithLambda { someIntegerValue ->
myClassInstance.extend(someIntegerValue)
}
}
}
As a workaround you can create a separate normal function and invoke it from an inline extension method:
inline fun View.isVisibility(v: Int): Boolean = isViewVisibility(this, v)
fun isViewVisibility(v: View, k: Int): Boolean = (v.visibility == k)
You can't call directly the extension method because you don't have the implicit this object available.
Using either a type with two parameters (the first for the implicit receiver, as #Bakawaii has already mentioned) or an extension type should both work without any warnings at all.
Let's take this function as an example:
fun String.foo(f: Int) = true
You can use assign this to a property that has a two parameter function type like this:
val prop: (String, Int) -> Boolean = String::foo
fun bar() {
prop("bar", 123)
}
Or, you can use an extension function type, that you can then call with either of these two syntaxes:
val prop2: String.(Int) -> Boolean = String::foo
fun bar2() {
prop2("bar2", 123)
"bar2".prop2(123)
}
Again, the above should all run without any errors or warnings.