This question is about fun() vs a lambda block definitions and scopes.
i have tried define the expressions in two ways. Here is what i have tried:
val myFunction = fun(){
println("i am in a function")
}
//but i also tried doing this:
val myFunction = {
println("i am in a lambda")
}
my problem is i do not know if they are equivalent and same thing ?
The differences are best described in https://kotlinlang.org/docs/reference/lambdas.html#anonymous-functions:
Anonymous functions allow you to specify return type, lambdas don't.
If you don't, return type inference works like for normal functions, and not like for lambdas.
As #dyukha said, the meaning of return is different:
A return statement without a label always returns from the function declared with the fun keyword. This means that a return inside a lambda expression will return from the enclosing function, whereas a return inside an anonymous function will return from the anonymous function itself.
There is no implicit it parameter, or destructuring.
Your specific cases will be equivalent, yes.
See the reference: https://kotlinlang.org/docs/reference/lambdas.html
There are several ways to obtain an instance of a function type:
Using a code block within a function literal, in one of the forms:
a lambda expression: { a, b -> a + b },
an anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 }
Both give you a function object which can be used interchangeably
Related
Edited: What and how it work? This is a kotlin dsl language.
fun ClassName.funcName(): (Type ) -> Type = {func body}
um this is one of the implementation I found, and also see the concrete syntax to this link: https://dzone.com/articles/the-complete-custom-gradle-plugin-building-tutoria
private fun CodeLinesExtension.buildFileFilter():
(File) ->
Boolean =
if (fileExtensions.isEmpty()) {
{ true }
} else {
{ fileExtensions.contains(it.extension) } // filter by extension
}
if you CALL this one as a arguments in ".filter(...)", what would happed in the "(File)" syntax, is it automatically receive an arguments?
someFiles.filter(CodeLinesExtension.buildFileFilter()).forEach{...}
There are many things at play here.
fun SomeClass.funcName() is the syntax for declaring extension functions. These are used just like regular methods of the class SomeClass, but can be defined outside of the class, and it's especially useful on classes that you don't control.
The return type of the function you mention is (File) -> Boolean. This expression describes a function type. In this specific case, it is the type of all functions that take a File as single argument, and return a Boolean.
This means that your buildFileFilter() function is an extension function that itself returns a function (File) -> Boolean.
As you can see in the function's body, inside the if statement, there are braces to define the blocks of the if-else ({ ... }), but also braces inside those blocks. This is because Kotlin uses braces for lambda expressions (literals for anonymous functions).
{ true } is a lambda expression that represents a function that may or may not take arguments, but returns true every time.
So this code:
if (fileExtensions.isEmpty()) {
{ true }
} else {
{ fileExtensions.contains(it.extension) } // filter by extension
}
returns either:
{ true } - a function that takes a File as argument and always returns true, or
{ fileExtensions.contains(it.extension) } - a function that takes a File as argument and returns whether fileExtensions contains the extension of the File that it receives (it is the implicit name for the File argument)
Note: the fact that these functions take a File as argument is inferred by the compiler based on the return type of buildFileFilter().
if you CALL this one as a arguments in ".filter(...)", what would happed in the "(File)" syntax, is it automatically receive an arguments?
filter() itself actually expects a function as argument. The function you pass to filter() is called a predicate, because it takes one element of the list as argument and returns a Boolean that says whether this element should be included in the resulting list.
So if you apply filter to a List<File>, the predicate expected by filter is exactly of type (File) -> Boolean (it says whether we should put this File item in the resulting collection).
Calling buildFileFilter() returns a function as we have seen, and the function is perfectly of the right type to use it in filter. The File argument of the function returned by buildFileFilter() will be given by the filter function itself when it calls your filter function.
Kotlin has these 2 features and I think there're no significant differences between these two
regardless of :
syntax
// lambda
val toUpper = { value: String ->
if (value.isEmpty()) "empty value"
else value.toUpperCase()
}
// anonymous func
val toUpper = fun(value: String): String {
if (value.isEmpty()) return "empty value"
else return value.toUpperCase()
}
flexibility to use return statement on anonymous function
I'm still digesting these features and hope you guys can help me pass through it.
Thanks.
Two differences according to Kotlin Reference:
(I think the more significant one out of the two) An anonymous function is still a function, so returning from it behaves the same as returning from any function. Returning from a lambda, however, actually returns from the function enclosing the lambda.
The return type of a lambda is inferred, while you can explicitly specify a return type for an anonymous function.
This website states: "Lambdas Expressions are essentially anonymous functions that we can treat as values – we can, for example, pass them as arguments to methods, return them, or do any other thing we could do with a normal object." (link)
So the answer is no, there isn't a difference between the two, they are interchangeable. Both of the codes you show above, will in the end return the same value to that variable
Besides the differences pointed by #jingx, you can not local return from a lambda that is not inlined.
So, the next snippet will not compile unless you add inline to the extension() function, thus, instructing the compiler to copy the function's content whenever it is referenced:
fun doSomethingWithLambda(){
10.extension{
if(it == 10)
return//compiler error
}
println("hello")
}
fun Int.extension(f: (Int)->Unit){
f(this)
}
I'm a novice of Kotlin.
I found that I can use another function without parameters even if it has.
Let me know when I can use it.
Q1) Why can I use 2 types? (with parameters & without parameters) Is it Kotlin's feature?
Q2) What does it mean? ((Result!) -> Unit)!
It seems you are confused, you can never use a function without arguments. If the function has arguments then you have to fill the slot somehow.
The closest thing that could relate to what you are referring to is default values for arguments.
fun example(boolean: Boolean = true) {}
example()
example(true)
example(false)
You can omit the argument because it has defaulted in the function signature.
The documentation
What you are showing in the image file is a lambda.
In the first example:
signIn(listener: Session...)
That seems to be a callback. So it is gonna be an interface or maybe an abstract class called when some async operation is finished.
The second example, it is the callback implemented as a lambda
signIn() { result ->
//do something
}
In Kotlin the last argument if it is a lambda or something that can be implemented as a lambda can be moved out of the parenthesis for syntactic sugar. A lambda is like an anonymous function, it is a literal of a function.
By example you can declare a lambda:
val lambda = {text: String -> text.lenght % 2 == 0}
fun setRuleForText(rule: (String)-> Boolean) {...}
setRuleForText(lambda)
setRuleForText() { text: String
text.isNotEmpty()
}
In this case, the argument is a kotlin function. The argument rule is a function that receives a String as an argument and returns Boolean. Something to point out is that expressions return the last written value without the need for the reserved return word.
This is the documentation. And here you can see from a good source more about functions (the author is a Kotlin certified trained by Jetbrains)
In your case (Result) -> Unit means the lambda should receive a Result type as argument and then return Unit (unit is like void in Java but is more than that), no explicit return type.
signIn() { result ->
//do something
}
Most of the types, the argument on lambdas is inferred automatically, but if not, then
signIn() { result: Result ->
//do something
}
Both of the listed functions take a parameter.
The first is:
signIn(listener: Session.SignInListener!)
That takes a single parameter, of type Session.SignInListener. (The ! means that Kotlin doesn't know whether it's nullable or not, because it's from Java and isn't annotated.)
The other is:
signIn {...} (listener: ((Result!) -> Unit)!)
That's the IDE's representation of the function with this signature:
signIn(listener: ((Result!) -> Unit)!)
That takes a single parameter, which is a function type (see below).
The reason the IDE shows it with braces is that in Kotlin, if the last parameter to a function is a lambda, you can move it outside the parentheses. So a call like this:
signIn({ println(it) })
could equally well be written like this:
signIn() { println(it) }
The two forms mean exactly the same. And, further, if the lambda is the only parameter, you can omit the parens entirely:
signIn { println(it) }
Again, it means exactly the same thing.
That syntax allows you to write functions that look like new language syntax. For example:
repeat (10) {
// ...
}
looks like a new form of loop construct, but it's really just a function called repeat, which takes two parameter (an integer, and a lambda).
OK, let's look at that function type: ((Result!) -> Unit)!
That's the type of a function which takes one parameter of type Result, and returns Unit (i.e. nothing useful; think of it as the equivalent of void). Again, it's complicated because Kotlin doesn't know about the nullability; both the Result parameter and the parameter holding this function have !s to indicate this. (Without them, it would just be (Result) -> Unit.)
Based on my understanding, anonymous function in Kotlin allow you to specify return type. In addition to that, return statement inside anonymous will exit only the function block, while in lambda it will exit the enclosing function.
Still, I can't imagine what would be the real world use case of anonymous function in Kotlin that lambda syntax cannot provide?
Kotlin Higher Order Function and Lambda
The use case is that sometimes we may wish to be explicit about the return type. In those cases, we can use so called an anonymous function. Example:
fun(a: String, b: String): String = a + b
Or better return control like:
fun(): Int {
try {
// some code
return result
} catch (e: SomeException) {
// handler
return badResult
}
}
Anonymous functions (a.k.a function expressions) are very handy when you have to pass a huge lambda with complex logic and want early returns to work. For example if you write a dispatcher in spark-java:
get("/", fun(request, response) {
// Your web page here
// You can use `return` to interrupt the handler
})
To simplify my real use case, let's suppose that I want to find the maximum number in a list:
var max : Int? = null
listOf(1, 2, 3).forEach {
if (max == null || it > max) {
max = it
}
}
However, compilation fails with the following error:
Smart cast to 'Int' is impossible, because 'max' is a local variable that is captured by a changing closure
Why does a changing closure prevent smart cast from working in this example?
In general, when a mutable variable is captured in a lambda function closure, smart casts are not applicable to that variable, both inside the lambda and in the declaring scope after the lambda was created.
It's because the function may escape from its enclosing scope and may be executed later in a different context, possibly multiple times and possibly in parallel. As an example, consider a hypothetical function List.forEachInParallel { ... }, which executes the given lambda function for each element of the list, but in parallel.
The compiler must generate code that will remain correct even in that severe case, so it doesn't make an assumption that the value of variable remains unchanged after the null check and thus cannot smart cast it.
However, List.forEach is quite different, because it is an inline function. The body of an inline function and the bodies of its functional parameters (unless the parameter has noinline or crossinline modifiers) are inlined at the call site, so the compiler could reason about the code in a lambda passed as an argument to inline function as if it was written directly in the calling method body making the smart cast possible.
It could, but currently, it doesn't. Simply because that feature is not implemented yet. There is an open issue for it: KT-7186.
Thanks to Ilya for the detailed explanation of the problem!
You can use the standard for(item in list){...} expression like this:
var max : Int? = null
val list = listOf(1, 2, 3)
for(item in list){
if (max == null || item > max) {
max = item
}
}
This looks like a compiler bug to me.
If the inline lambda parameter in forEach were marked as crossinline then I would expect a compilation error because of the possibility of concurrent invocations of the lambda expression.
Consider the following forEach implementation:
inline fun <T> Iterable<T>.forEach(crossinline action: (T) -> Unit): Unit {
val executorService: ExecutorService = ForkJoinPool.commonPool()
val futures = map { element -> executorService.submit { action(element) } }
futures.forEach { future -> future.get() }
}
The above implementation would fail to compile without crossinline modifier. Without it, the lambda may contain non-local returns which means it cannot be used in a concurrent fashion.
I suggest creating an issue: Kotlin (KT) | YouTrack.
The problem is that foreach creates multiple closures, each of which access the same max which is a var.
What should happen if max were set to null in another of the closures after the max == null check but before it > max?
Since each closure can theoretically work independently (potentially on multiple threads) but all access the same max, you can't guarantee it won't change during execution.
As this is in the top results for the error Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure here is a general solution that worked for me (even if the closure is not inlined):
Sample showing this error:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
if(lastLine != null) {
println(lastLine.length) // error! lastLine is captured by the useLines closure above
}
Fix: Create a new variable that is not captured by the closure:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
val finalLastLine = lastLine
if(finalLastLine != null) {
println(finalLastLine.length)
}