named functions vs lambda reflexion

I am still learning Kotlin and trying to understand its core principles. What I do not get is this:

fun x() : Int { return 10 }
val y : () -> Int = ::x
val z : () -> Int = { 10 }

fun main(args: Array<String>) {
    println(::x) 
    println(y)   
    println(z)    
}

we get the following output:

fun x(): kotlin.Int
fun x(): kotlin.Int
() -> kotlin.Int

My question is why the output is not the same (I believed these functions should be interchangeable, equivalent)? I think the type of all the functions should be () -> Int. Why do we keep the original name with the function signature (fun x) even though it is assigned to a different name (y)? Is there any language design principle which would require such difference in function signatures?

And a bonus question - why do we need to use the operator ::. It does not compile without it. But why is this required by the language design? Would not val y = x work just fine and be much simpler?

Upvotes: 0

Views: 617

Answers (3)

Silvestr
Silvestr

Reputation: 809

I think for better understanding look at bytecode

  private final static Lkotlin/jvm/functions/Function0; y
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x1A
  // signature Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
  // declaration: kotlin.jvm.functions.Function0<java.lang.Integer>
  private final static Lkotlin/jvm/functions/Function0; z
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x19
  public final static main([Ljava/lang/String;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ALOAD 0
    LDC "args"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
   L1
    LINENUMBER 6 L1
    GETSTATIC MainKt$main$1.INSTANCE : LMainKt$main$1;
    ASTORE 1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L2
    LINENUMBER 7 L2
    GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
    ASTORE 1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L3
    LINENUMBER 8 L3
    GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
    ASTORE 1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L4
    LINENUMBER 9 L4
    RETURN
   L5
    LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x19
  public final static x()I
   L0
    LINENUMBER 11 L0
    BIPUSH 10
    IRETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  // signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
  // declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getY()
  public final static getY()Lkotlin/jvm/functions/Function0;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 12 L0
    GETSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x19
  // signature ()Lkotlin/jvm/functions/Function0<Ljava/lang/Integer;>;
  // declaration: kotlin.jvm.functions.Function0<java.lang.Integer> getZ()
  public final static getZ()Lkotlin/jvm/functions/Function0;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 13 L0
    GETSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 12 L0
    GETSTATIC MainKt$y$1.INSTANCE : LMainKt$y$1;
    CHECKCAST kotlin/jvm/functions/Function0
    PUTSTATIC MainKt.y : Lkotlin/jvm/functions/Function0;
   L1
    LINENUMBER 13 L1
    GETSTATIC MainKt$z$1.INSTANCE : LMainKt$z$1;
    CHECKCAST kotlin/jvm/functions/Function0
    PUTSTATIC MainKt.z : Lkotlin/jvm/functions/Function0;
    RETURN
    MAXSTACK = 1
    MAXLOCALS = 0
}

y and z value have type Function0

static <clinit>()V // here we have static initialization block where y and z are initialized

x have type MainKt$main$1

In each case(println) we just show method declaration(visibility modifiers, return type, signature) without invocation. y and z are high order function that in bytecode are presented by Function0 class, for x value it's just static function.

And when you invoke println(::x) println(y) println(z) you just print functions declarations. This is ok, that declaration of x and y functions are different that in z function. Because y has reference to x function and z are still high order function that return 10. In simple words y == x, because you assign to y function declaration from x. Just for Note. In Java I can show method declaration in this way:

  public static void main(String[] args) {
    try {
      System.out.println(Main.class.getMethod("A", null)); // prints public static void Main.A()
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }
  }

  public static void A() {
    System.out.println(15);
  }

Conclusion: In your code you just print function return type, visibility modifier and signature.

Upvotes: 0

holi-java
holi-java

Reputation: 30686

IF you think from my point of view, I think you can understand it immediately.

  • x just is an identifier not a variable, so you can't reference it directly.
  • fun x() is a class which derived from Function0.
  • the expression ::x is the instance of the type of fun x(), this is called function reference expression in kotlin.

Kotlin makes functions and properties first-class citizens in the language, and introspecting them.

sometimes function has it own class in kotlin like as java-8 lambda expression. but function reference expression can't used with the diff receiver.

when calling an inline function followed with a lambda, both the lambda & the function will be inlined at call-sites. but call with a non-inline function reference expression, only the inline function will be inlined at call-sites.

Upvotes: 0

Miha_x64
Miha_x64

Reputation: 6373

fun x() is an ordinary named function from procedural programming. val y is a property which holds a reference to x. val z is an anonymous functional from functional programming.

:: is a 'function reference', a kind of bridge between procedural and functional programming.

By default, your functions should be fun. Lambda expressions (anonymous functions), on the other side, are intended to be passed to other functions as callbacks.

Upvotes: 1

Related Questions