Type name as value? - kotlin

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.

Related

Correct understanding of T.() lambda

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.

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.

Kotlin higher order function parameters: Passing subtypes

I have run into a problem with function parameters in Kotlin. I will explain the issue with the help of some code.
I created a class hierarchy. When I pass a subtype into a function expecting a parent type, there is no issue.
open class A (val i: Int)
class B (val j: Int) : A(j)
fun f(x: A){
print(x)
}
fun test_f(){
f(A(1))
f(B(1)) //no problem
}
I tried to mimic this with function parameters.
fun g(x: (A)->Int){
print(x)
}
fun test_g(){
val l1 = { a: A -> a.hashCode()}
g(l1)
val l2 = { b: B -> b.hashCode()}
g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}
It seems that function type (B) -> Int is not a subtype of (A) -> Int.
What is the best way to address this?
My original problem is to define a higher order function in A.h that takes a function z: (A) -> X as parameter. And I want call h on an object of type B and pass a function z: (B) -> X.
Update:
I tried generics with upper bound, yet my issue is not solved. Please find code below:
// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
fun <M: A> g(x: (M)->Int){
print(x(this)) // Error: Type mismatch. Expected: M, Found: A
}
}
You can solve it using generics and an extension function on a generic receiver. Deriving the extension function from your updated sample:
fun <T : A> T.g(x: (T)->Int){
print(x(this))
}
This way it is ensured that the receiver and the first parameter type of the given function are the same, which is either an A or a subtype of it.
What you're trying to do is a conversion from function type (B) -> Int (source) to (A) -> Int (target). This is not a safe conversion.
Your source function (B) -> Int takes any instance which is a B, but not necessarily an instance of type A. More concretely, it cannot handle all arguments that are of type A but not of type B.
Imagine your classes look like this:
open class A
class B : A {
fun onlyB() = 29
}
You can define a function
val fb: (B) -> Int = { it.onlyB() }
val fa: (A) -> Int = fb // ERROR
The function fb will not be able to operate on class A, since A does not have the onlyB() function. As a consequence, you're not allowed to convert it to a function type which takes A parameters.
This concept is called contravariance, meaning that input parameters can only be constrained by becoming more concrete, not more abstract. So, the opposite direction works:
val fa: (A) -> Int = { it.hashCode() }
val fb: (B) -> Int = fa // OK, every B is also an A
In contrast, for return values, the concept of covariance applies. This means that return values are allowed to become more abstract, but not more concrete:
val fInt: (B) -> Int = { it.onlyB() }
val fNum: (B) -> Number = fInt // OK, every Int is also a Number
These relations can be exploited in generic classes, using Kotlin's in (contravariance) and out (covariance) keywords -- see here for detailed explanation.

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

How to save a function reference as the value in a Map type, and invoke it with a parameter later on in Kotlin?

val specials:Map<String, (Any)->Unit> = mapOf(
"callMe1" to {asParam1()},
"callMe2" to {asParam2()}
)
fun asParam1(num:Int) {
println(num)
}
fun asParam2(text:String) {
println(text)
}
fun caller() {
specials["callMe1"]?.invoke("print me")
specials["callMe2"]?.invoke(123)
}
fun main(args: Array<String>) {
caller()
}
My requirement is simple, I want to save the function asParam1 and asParam2 as a value in the variable specials. And invoke it later on by fetching the value from a Map.
However, the compiler doesn't like it:
Error:(1, 40) Type inference failed. Expected type mismatch: inferred
type is Map Unit> but Map Unit> was
expected
Error:(1, 69) No value passed for parameter num
Error:(1, 96) No value passed for parameter text
While this task is pretty simple in a weak typed language, I don't know how to do in Kotlin. Any help would be welcome. Thanks!
The correct syntax is "calllme" to ::asParam1.
But then the signatures will be wrong because the Map expects type (Any)->Unit and yours have (Int)->Unit and (String)->Unit. Here is an example that does not produce the error:
val specials:Map<String, (Any)->Unit> = mapOf(
"callMe1" to ::asParam1,
"callMe2" to ::asParam2
)
fun asParam1(num:Any) {
if(num is Int) println(num)
}
fun asParam2(text:Any) {
if(text is String) println(text)
}
fun caller() {
specials["callMe2"]?.invoke("print me")
specials["callMe1"]?.invoke(123)
}
Keep in mind, your code for the caller has special knowledge about how to call each of your functions (i.e., the correct parameter types), but the compiler does not have this same knowledge. You could accidentally call asParam1 passing a String instead of an Int (which is what your caller function was doing, I fixed it in my example) and that is not allowed. Which is why I changed the signatures of both asParam* to accept Any parameter, and then validated the expected type in each function (ignoring bad types).
If your intent is to pass integers in addition to strings to asParam2(), then change the body to test for both Int and String and convert the integer to a string.
When you write { asParam1() }, you create a lambda with an executable code block inside it, so you need to properly call the function asParam1(...), which requires an Int argument.
So, the first change you need to make is: { i -> asParam1(i) }.
But this code will still not pass the type checking, because, matching the type of the map, the lambda will be typed as (Any) -> Unit (the values in the map should all be able to accept Any, and a function that expects a narrower type cannot be a value in this map).
You then need to convert the Any argument to Int to be able to invoke the function: { i -> asParam1(i as Int) }
Finally, the map will look like this:
val specials: Map<String, (Any) -> Unit> = mapOf(
"callMe1" to { i -> asParam1(i as Int) },
"callMe2" to { s -> asParam2(s as String) }
)
The invocation stays unchanged, as in your code sample.
The function reference syntax (::asParam1) would allow you to reference a function that already accepts Any, it would not implicitly make the conversion described above. To use it, you would have to modify your functions to accept Any, as in #Les's answer.