Kotlin Data Type Syntax - kotlin

What does the following data type means:
() -> String
It is being used in the following context.
fun sampleFun(message: ()-> String)

this is a lambda function, this code means that this is a lambda function that return a string:
() -> String
and here it's passed as a parameter named message:
fun sampleFun(message: ()-> String)
so when you call sample fun you need to add message parameter like that:
sampleFun(
message = {
"Hello" // this means that when you call message it will return this String
}
)
take a look on this link if want to learn more about lambda functions in kotlin and how to use them correctly

Related

How to use the lambda in kotlin ->

Have a question about -> in kotlin.
fun test0 (a: String, combine: (b: String) -> String ): String {
return combine(a)
}
There is a example function above, how to use this function?
I dont know how to pass the parm combine.
Have tried follow:
private fun getFold(a: String): String {
return a
}
fun test() {
var a: String = "a"
val test1 = test0 (a, combine = getFold(a))
}
Sync error in combine = getFold(a)) ,say:
Type mismatch. Required:(String) → String Found: String
So how to pass the parm combine?
In your example, combine is a parameter of type (b: String) -> String, which is a function taking a single String and returning a String.
(In fact, you don't need to name the parameter b; it could simply be written (String) -> String.)
There are three main ways to pass a function as a parameter:
As a lambda. This is perhaps the most common: you provide the function body directly, in braces. For example:
val test1 = test0(a, { it })
Here the lambda is { it }. (it is a keyword you can use in lambdas which take a single parameter, and refers to that parameter. An equivalent might be { a -> a }.)
In fact, because the function parameter is the last parameter, Kotlin lets you move the lambda after the parens:
val test1 = test0(a){ it }
This means exactly the same, but in some cases it can read better (by making the function call look like language syntax; functions like map and filter are often called that way).
As an anonymous function. This is similar to a lambda, but the syntax is more like defining a normal function; it's more verbose, but gives you more control. In this case you could use a normal function body:
val test2 = test0(a, fun(b: String): String { return b })
Or an expression body:
val test2 = test0(a, fun(b: String) = b)
As a callable reference. This is for when you have an existing method or function that you want to use. (You might already have a suitable function in your project, or in a library; or the code might be too long to fit neatly into a lambda.)
It's done using the :: operator, e.g.:
val test3 = test0(a, ::getFold)
You can reference a method, top-level function, extension function, property, or constructor in this way. (If it's not a top-level function, you may need to tell the compiler which object/class it belongs to — just as you would when calling it directly — e.g. myInstance::methodName.)
It looks like this is what the code in the question is trying to do. However, the lambda syntax is usually simpler and clearer, so consider that if you don't need the existing function for any other reason.
(The error message in the question is because combine = getFold(a) will call getFold(), and try to assign its result to the combine parameter. That result is a String, not a function type, hence the type mismatch.)
test0(a, combine = ::getFold)
or
test0(a, combine = this::getFold)
Or like this:
combine = {getFold(it)}

Having trouble passing a value in a Kotlin function. The variable being convert:(String)->R

I have some code here that reads a file and converts it to a list of a type depending what is being passed to the function. The code for this function looks like so.
fun <R> readFile(path:String,convert:(String)->R): List<R> {
val reader = BufferedReader(FileReader(File(path)))
var line = reader.readLine()
val result = mutableListOf<R>()
while(line!=null) {
result.add(convert(line))
line = reader.readLine()
}
reader.close()
return result.toList()
}
The problem I have is that I am unsure sure what to pass into the variable convert. I am trying to return back a list of Person objects back. Below is the code I tried myself to hit the function readFile
val listOfPerson = readFile("path/person.txt","Person")
With this I'm getting an error with my Type. Intellij is flagging "Required (String) -> ???"
The type (String) -> R is a function type. It represents a function that takes a String as argument and returns R (which is generic and can be different on every call to readFile()).
In order to call readFile(), you need to pass a String and a function:
readFile("path/person.txt") { line -> /* whatever you need */}
Note that in Kotlin, the { line -> ...} is a lambda expression, i.e. a function literal. This particular lambda expression represents a function that takes the argument line and does whatever is in the body.
Lambda expressions can be passed out of the parentheses of the function call, but it's still an argument to the function. The above code is equivalent to:
readFile("path/person.txt", { line -> /* whatever you need */})
In your specific case, the convert argument is a conversion function, so you need to provide some code that converts the line: String argument into whatever R type you need in the output list.
If you just want a list of the lines as strings (without conversion) you can simply provide the identity function:
readFile("path/person.txt") { line -> line }
Or in short:
readFile("path/person.txt") { it }
it is an implicit argument (a shortcut) for lambda expressions that represent functions with only 1 argument, which is the case here.

Kotlin | Find param of a function which returns a lambda using the lambda

Say I have a function haveFun which takes in a Method (from java.lang.reflect package) as a param and returns a lambda as below
typealias AnyFun = (o: Any?) -> Any?
fun haveFun(method: Method): AnyFun {
return { o -> method.invoke(o) }
}
data class Game(val name: String)
Now if I pass a method to the function and assign the lambda to a field as
val game = haveFun(Game::name.javaGetter!!)
Can I find out and access the Method that was passed to the function using the above game field which is a lambda?
I can see the Method while debugging on Intellij, but not sure on how to access it.
You can access and use it through reflection as a declaredField having $method name, as follows:
val methodField = game.javaClass.getDeclaredField("\$method")
val method = methodField.get(game) as Method
println(method.invoke(Game("Pietro"))) // outputs the String "Pietro"

Using implicit variable in function literal

I started reading Kotlin course book. I stopped on function literals. Here I have a code:
val printMessage = { message: String -> println(message) }
printMessage("hello")
printMessage("world")
Then I have an information that I can omit parameter type:
{ message -> println(message) }
And now I have next step:
"In fact, Kotlin has a neater trick. If there is only a single parameter and the type can beinferred, then the compiler will allow us to omit the parameter completely. In this case, itmakes the implicit variable it available:
{println(it)}
And now after using this code I get an error "unresolved reference: it" and "too many arguments for public operator fun invoke(): ??? defined in kotlin.Function()":
val printMessage = {println(it)}
printMessage("print something")
My question is how to use implicit variable in single paramenter function literal?
See the Kotlin documentation, specifically where it says:
If the compiler can figure the signature out itself, it is allowed not
to declare the only parameter and to omit ->. The parameter will be
implicitly declared under the name it.
In your case, the compiler (at least up to current version 1.3.31) can't figure the signature out itself:
val printMessage = {println(it)}
But if you give your printMessage variable an explicit type, it will work:
val printMessage: (String) -> Unit = { println(it) }
You always need to provide all information about all generic parameters. If you want to omit it, it needs to be inferable from some other part of the code. The only information you provide though is that you want printMessage to be a lambda. So it assumes it to be of type ()->Unit. This is because you don't declare a parameter for the lambda itself. The implicit parameter it is therefore not usable.
val printMessage = { it: String -> println(it) }
val printMessage: (String)->Unit = { println(it) }
Simply put: If you're inside a lambda with one parameter, the implicit it can be used as this parameters name, but a reference named it within the body of the lambda doesn't declare the single parameter.

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.