Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function definition: fun vs val

Tags:

kotlin

I'm curious about what is the suggested way to define member functions in Kotlin. Consider these two member functions:

class A {

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42

}

These appear to accomplish the same thing, but I found subtle differences.

The val based definition, for instance, seems to be more flexible in some scenarios. That is, I could not work out a straight forward way to compose f with other functions, but I could with g. To toy around with these definitions, I used the funKTionale library. I found that this does not compile:

val z = g andThen A::f // f is a member function

But if f were defined as a val pointing to the same function, it would compile just fine. To figure out what was going on I asked IntelliJ to explicitly define the type of ::f and g for me, and it gives me this:

val fref: KFunction1<Int, Int> = ::f

val gref: (Int) -> Int = g

So one is of type KFunction1<Int, Int>, the other is of type (Int) -> Int. It's easy to see that both represent functions of type Int -> Int.

What is the difference between these two types, and in which cases does it matter? I noticed that for top-level functions, I can compose them fine using either definition, but in order to make the aforementioned composition compile, I had to write it like so:

val z = g andThen A::f.partially1(this)

i.e. I had to partially apply it to this first.

Since I don't have to go through this hassle when using vals for functions, is there a reason why I should ever define non-Unit member functions using fun? Is there a difference in performance or semantics that I am missing?

like image 538
Matthias Avatar asked Mar 20 '16 19:03

Matthias


People also ask

What is the difference between def and val in Scala?

That's all about the difference between var, val, and def in Scala. In short, the val and var are evaluated when defined, while def is evaluated on call. Also, val defines a constant, a fixed value that cannot be modified once declared and assigned while var defines a variable, which can be modified or reassigned.

What is fun function in Java?

main() is a static method and fun() is a non-static method in class Main. Like C++, in Java calling a non-static function inside a static function is not allowed.

What does fun mean in Kotlin?

In Kotlin functions are declared with the fun keyword and they are first-class citizen. It means that functions can be assigned to the variables, passed as an arguments or returned from another function.


2 Answers

Kotlin is all about Java interoperability and defining a function as a val will produce a completely different result in terms of the interoperability. The following Kotlin class:

class A {
  fun f(x: Int) = 42
  val g = fun(x: Int) = 42
}

is effectively equivalent to:

public class A {
  private final Function1<Integer, Integer> gref = new Function1<Integer, Integer>() {
    @Override
    public Integer invoke(final Integer integer) {
      return 42;
    }
  };

  public int f(final int value) {
    return 42;
  }

  public Function1<Integer, Integer> getG() {
    return gref;
  }
}

As you can see, the main differences are:

  1. fun f is just a usual method, while val g in fact is a higher-order function that returns another function
  2. val g involves creation of a new class which isn't good if you are targeting Android
  3. val g requires unnecessary boxing and unboxing
  4. val g cannot be easily invoked from java: A().g(42) in Kotlin vs new A().getG().invoke(42) in Java

UPDATE:

Regarding the A::f syntax. The compiler will generate an extra Function2<A, Integer, Integer> class for every A::f occurrence, so the following code results in two extra classes with 7 methods each:

val first = A::f
val second = A::f

Kotlin compiler isn't smart enough at the moment to optimize such kind of things. You can vote for the issue here https://youtrack.jetbrains.com/issue/KT-9831. In case you are interested, here is how each class looks in the bytecode: https://gist.github.com/nsk-mironov/fc13f2075bfa05d8a3c3

like image 196
Vladimir Mironov Avatar answered Oct 19 '22 07:10

Vladimir Mironov


Here's some code showing how f and g are different when it comes to usage:

fun main(args: Array<String>) {
    val a = A()
    exe(a.g)  // OK
    //exe(a.f)  // does not compile
    exe { a.f(it) }  // OK
}

fun exe(p: (Int) -> Int) {
    println(p(0))
}

Where f and g are:

  fun f(x: Int) = 42

  val g = fun(x: Int) = 42

You can see that g is an object that can be used like a lambda, but f cannot. To use f similarly, you have to wrap it in a lambda.

like image 3
Doug Stevenson Avatar answered Oct 19 '22 07:10

Doug Stevenson