Reputation: 69
I have the following code in Kotlin:
fun iterate(first: String, second: String, iter: Int, func: (String, String) -> String) {
for(i in 0..iter)
println(func(first, second))
}
fun concatenation(one: String, two: String): String {
return "$one $two";
}
fun main(args: Array<String>) {
iterate("Me", "You", 5, ::concatenation)
iterate("Another", "One", 6) {a, b -> a + b}
}
Can anyone explain what's happening in curly brackets in the second call of iterate function? Am I overriding it?
Also, I tried to swap func
and iter
paratemers in function declaration. This way I am not able anymore to use curly brackets in the second call and insert a code, because I don't know where to put iter argument.
Upvotes: 0
Views: 38
Reputation: 18577
Your function iterate()
takes four parameters: two Strings, and Int, and a function.
When you call iterate()
, you can give the String arguments in several ways: for example, you could give the name of a variable holding the String (such as one
); or you could give a literal String value (such as "Me"
).
And you can give the function argument in both of those ways, too.*
The equivalent of the first way is to give a reference to an existing function. That's what ::concatenation
is. (::
is used to create function references.)
The equivalent of the second way is to use a lambda, which is a way of writing out the contents of a function. {a, b -> a + b}
is a lambda: it's a function which takes two parameters, and returns their concatenation. (In this case the compiler can tell from the context that the two parameters must be Strings; in other cases, you might need to specify their types.)
What's tricky about the code in this question is that the lambda doesn't look like it's being passed as an argument to the function! It's outside the parens:
iterate("Another", "One", 6) {a, b -> a + b}
And this is a feature that's specific to Kotlin: if the last parameter is a function, you can put the lambda after the close paren. (And, if that's the only argument, you can omit the parens entirely.)
So it means exactly the same as:
iterate("Another", "One", 6, {a, b -> a + b})
This may look confusing, but it supports function calls that look like new language syntax. For example, you may have used the with()
function:
with (someObject) {
// In here, ‘this’ refers to someObject.
}
That's not a keyword in the language; it's simply a normal function which takes its action as the last parameter.
So I bet you can now see the answer to your last question. If you were to swap the iter
and func
params around, you'd have to call it like:
iterate("Another", "One", {a, b -> a + b}, 6)
(Because func
is no longer the last parameter, it has to be inside the parens now.)
(* There are additional ways to specify string params and function params, too, but there's no room to go into them all here!)
Upvotes: 1
Reputation: 19554
Your func
parameter's type is a function - its type is (String, String) -> String
which means it takes two String
parameters (in the parentheses) and returns a String
(after the arrow).
That's what all function types look like, (x) -> y
. A function type that takes no parameters and doesn't return a value has a type () -> Unit
(because every function returns something in Kotlin, Unit
is the "no meaningful value" type).
So for your func
parameter, you can pass in any function with that type - takes two strings, returns a string. Your concatenation
function matches that, so you can pass it in - you're using a function reference, ::concatenation
which is a way to pass that function - it's a reference to it.
With your second iterate
call, you're creating a function object to pass in, instead of referring to one that's declared as an actual method on the class. You're using a lambda that takes two parameters, a
and b
, and calls +
on them (and implicitly returns that value as the result).
Now you're not saying that they're String
s here (you could declare their types explicitly if you wanted), but the type system is basically checking that this can apply to two String
variables, and that the result of +
on those would also be a String
. So it all works fine, and it can be used in your iterate
function.
Like @IR42 says in the comments, you're using a trailing lambda - basically when the last parameter in a function call is a lambda, instead of keeping it in the parentheses, you can move it outside (and remove the parentheses if the lambda was the only thing in there).
But this only works when it's the last parameter - otherwise it wouldn't know for sure which one you'd moved out! - which is why you can't change the order and keep the trailing lambda. You can switch up the order, but the call will have to be like this:
iterate("Me", "You", ::concatenation, 5)
iterate("Another", "One", {a, b -> a + b}, 6)
Upvotes: 1