Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambdas assigned to variables in Kotlin. Why?

I noticed that I get the same effect if I define this trivial function:

fun double ( i: Int ) = i*2

and if I define a variable and assign a lambda (with an identical body) to it:

var double = { i : Int -> i*2 }

I get the same result if I call double(a) with either declaration. This leaves me confused. When is it needed, recommended, advantageous to define a variable as a lambda rather than define a function to it?

like image 833
Dakatine Avatar asked Oct 17 '18 07:10

Dakatine


3 Answers

When is it needed, recommended, advantageous to define a variable as a lambda rather than define a function to it?

Whenever you have the choice of either, you should use a fun declaration. Even with a fun you can still get a first-class callable object from it by using a function reference.

On the JVM, a fun is significantly more lightweight, both in terms of RAM and invocation overhead. It compiles into a Java method, whereas a val compiles into an instance field + getter + a synthetic class that implements a functional interface + a singleton instance of that class that you must fetch, dereference, and invoke a method on it.

You should consider a function-typed val or var only when something is forcing you to do it. One example is that you can dynamically replace a var and effectively change the definition of the function. You may also receive function objects from the outside, or you may need to comply with an API that needs them.

In any case, if you ever use a function-typed property of a class, you'll know why you're doing it.

like image 150
Marko Topolnik Avatar answered Sep 28 '22 13:09

Marko Topolnik


First, if I understand you right, your question is "Why are functions first-class citizens in Kotlin -- And when to use them as such?", right?

Kotlin functions are first-class, which means that they can be stored in variables and data structures, passed as arguments to and returned from other higher-order functions. You can operate with functions in any way that is possible for other non-function values. (see here)


As stated in the docs, one use case are higher-order functions. As a first step, I will leave the wikipedia link here: https://en.wikipedia.org/wiki/Higher-order_function

Basically, a higher-order function is a function that takes functions as parameters, or returns a function. This means that a higher-order function has at least one parameter of a function type or returns a value of a function type.

Following a short example of a higher-order function that receives a parameter of function type (Int) -> Boolean:

fun foo(pred: (Int) -> Boolean) : String = if(pred(x)) "SUCCESS" else "FAIL"

This higher-order function can now be called with any (Int) -> Boolean function.


The docs also state ... [can be used] in any way that is possible for other non-function values.

This means that you can, for example, assign different functions to a variable, depending on your current context.

For example:

// This example is verbose on purpose ;)
var checker: (Int) -> Boolean
if (POSITIVE_CHECK) {
    checker = { x -> x > 0 } // Either store this function ...
} else {
    checker = { x -> x < 0 } // ... or this one ...
}
if (checker(someNumber)) { // ... and use whatever function is now stored in variable "checker" here
  print("Check was fine")
}

(Code untested)

like image 37
Markus Weninger Avatar answered Sep 28 '22 11:09

Markus Weninger


You can define variable and assign it lambda when you want change behaviour for some reason. For example, you have different formula for several cases.

val formula: (Int) -> Int = when(value) {
    CONDITION1 -> { it*2 }
    CONDITION2 -> { it*3 }
    else -> { it }
}
val x: Int = TODO() 
val result = formula(x)

If you simply need helper function, you should define it as fun.

like image 20
ConstOrVar Avatar answered Sep 28 '22 12:09

ConstOrVar