I have the following function that uses TCO:
tailrec fun superDigit(n: String): Int {
val sum = n.fold(0) { sum, char -> sum + char.toString().toInt() }
return if (sum < 10) sum else superDigit(sum.toString())
}
If I implement the same function as an extension function like this:
fun String.superDigit(): Int {
val sum = fold(0) { sum, char -> sum + char.toString().toInt() }
return if (sum < 10) sum else sum.toString().superDigit()
}
Is the extension function tail call optimized as well?
IMO calling the extension function is still a regular function call with this as parameter so it's still a recursive call and since tailrec can't be used in extension functions, my assumption is that it's not been optimized by the compiler. Is this assumption correct or not?
since tailrec can't be used in extension functions
Are you sure?
I've just tested it by taking a look at Kotlin bytecode in IntelliJ IDEA. First of all, the code with tailrec on the extension function compiles successfully. Going further: compare the two pieces of Kotlin code and bytecode below, one with tailrec, and the other one without.
Kotlin:
fun Double.tailrecTestExtension(): Double
= (this - 1.0).tailrecTestExtension()
Bytecode:
// access flags 0x19
public final static tailrecTestExtension(D)D
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 13 L0
DLOAD 0
DCONST_1
DSUB
INVOKESTATIC com/example/TestKt.tailrecTestExtension (D)D
DRETURN
L1
LOCALVARIABLE $receiver D L0 L1 0
MAXSTACK = 4
MAXLOCALS = 2
Kotlin:
tailrec fun Double.tailrecTestExtension(): Double
= (this - 1.0).tailrecTestExtension()
Bytecode:
// access flags 0x19
public final static tailrecTestExtension(D)D
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 13 L0
DLOAD 0
DCONST_1
DSUB
DSTORE 0
GOTO L0
L1
LOCALVARIABLE $receiver D L0 L1 0
MAXSTACK = 4
MAXLOCALS = 2
Notice that in the first example there's INVOKESTATIC call (which corresponds to a regular recursion), which was replaced with a regular jump (GOTO) in the second version (which corresponds to a loop - expected behavior introduced by tailrec).
Note: I'm not an expert in Kotlin bytecode, my understanding is based on some basic knowledge about the assembly language. Here I assume that this knowledge is transferable to Kotlin bytecode.
Related
two functions test1 and test2, one uses "is" to check type, and one use "as?", seems test2 with "as?" has less code, but is it really better than the one uses "is" to do check?
Is there a comparison for using "is" vs. "as?", what is suggestion for general use of these two?
class Message(val int: Int, val msg:String)
class Test {
fun test1(objList: List<Any?>) {
for (i in objList.size - 1 downTo 0) {
val item = objList.get(i)
if (item is Message) {
println(item)
}
}
}
fun test2(objList: List<Any?>) {
for (i in objList.size - 1 downTo 0) {
(objList.get(i) as? Message)?.let {item ->
println(item)
}
}
}
}
So if you look at the JVM bytecode of both:
// JVM bytecode of test1
L5
LINENUMBER 8 L5
ALOAD 3
INSTANCEOF com/example/artifact/Message
IFEQ L6
// JVM bytecode of test2
L4
LINENUMBER 16 L4
ALOAD 0
ILOAD 1
INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object; (itf)
DUP
INSTANCEOF com/example/artifact/Message
IFNE L5
POP
ACONST_NULL
L5
CHECKCAST com/example/artifact/Message
DUP
IFNULL L6
ASTORE 3
You can clearly see that the test1 does check for the type only and it smart casts the result, while the test2 however first checks if the instance is of that type and then returns null or "casts" explicitly it to that type.
So, through this observation I'll suggest you to use the is operator as it is more optimized for these tasks. If you don't like using curly braces then you can omit them like python, or put the println on the same line as you're doing if conditions.
This is more optimized code if you'd like:
fun test1(objList: List<Any>) {
for (item in objList.asReversed()) {
if (item is Message) println(item)
}
}
fun test2(objList: List<Any>) {
for (item in objList.asReversed()) {
(item as? Message)?.let { println(it) }
}
}
The as? version may have one less line of code, but does it really have less code overall? You may have gotten rid of:
val item = objList.get(i)
But you replaced it with:
(objList.get(i) as? Message)?.let {item ->
In this case, I'd argue that the as? version requires ever so slightly more time to comprehend than the is version, which is more straightforward. I also doubt there's a significant difference in performance between the two approaches. In general, always prefer the more readable code over the more performant code unless you've profiled an actual performance problem in your application. Whether or not there's a performance problem is highly dependent on the context of your application. And remember, premature optimization is the root of all evil.
If you really want less code then consider using the following:
fun test(objList: List<Any>) = objList.asReversed().forEach {
if (it is Message) println(it)
}
This makes use of:
Single-expression functions
List.asReversed (returns a reversed view of the original list)
Iterable.forEach
it: implicit name of a single parameter (in lambda expressions)
is is type checking. as is type casting.
the different is
is return boolean, true or false
as return that type
you use is if you want the answer only yes or no
example
if (user is Login) you just want the answer yes or no right?
and then you do something else
you use as when you want to try to cast to that type
example
(user as? Admin).let{print(it.name.toString())}
this one you want to check if this user is Admin or not
if yes you want to use that user value to print something like name
so for condition Use is
for casting Use as
fun test(objList: List<Any>) = objList.asReversed().forEach {
when(it){ is Message -> println(it)}
}
In Scala, you can write
val x = {
... do some complex computations ..
42
}
to hide stuff inside of the code block.
The closest I came in Kotlin is:
val x = {
... do some complex computations ..
42
}()
Is there a better way?
EDIT:
isn’t run {} in the above example essentially the same
is calling run costly?
ANSWER:
using run {} inlines, whereas {}() does NOT (see my own answer below)
Use the run function. It takes a function as a parameter, runs it and returns the result.
val x = run {
... do some complex computations ..
42
}
The run function is inlined, so it will have no performance overhead.
Yes there is the run function.
val x = run {
...
42
}
And also you can use these methods too:
val a=1.also{
// your code
}
val b=2.apply{
// your code}
}
val c=3.let{
// your code
}
val d=4.runCatching{
// your code
}
To verify inlining when using run, I created a small example:
fun main() {
{
Math.random()
}()
run {
Math.random()
}
}
and viewed the produced bytecode:
public final static main()V
L0
LINENUMBER 2 L0
GETSTATIC TestKt$main$1.INSTANCE : LTestKt$main$1;
CHECKCAST kotlin/jvm/functions/Function0
INVOKEINTERFACE kotlin/jvm/functions/Function0.invoke ()Ljava/lang/Object; (itf)
POP
L1
LINENUMBER 6 L1
L2
L3
L4
ICONST_0
ISTORE 0
L5
LINENUMBER 7 L5
INVOKESTATIC java/lang/Math.random ()D
L6
L7
LINENUMBER 6 L7
L8
POP2
L9
LINENUMBER 9 L9
RETURN
L10
LOCALVARIABLE $i$a$-run-TestKt$main$2 I L5 L7 0
MAXSTACK = 2
MAXLOCALS = 1
So, indeed:
Calling via () calls a generated Function0-derived class that encapsulates the block as a lambda (label L0)
Calling via run fully inlines the block (label L5)
I know the idiom for consuming nullable Booleans in Kotlin is:
fun nullableBoolean(b: Boolean?) {
if (b == true) {
println("is true")
}
}
I'm curious about what happens under the hood. This is the interesting part of the generated bytecode for this function:
public final static nullableBoolean(Ljava/lang/Boolean;)V
#Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0
L0
LINENUMBER 4 L0
ALOAD 0
ICONST_1
INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
IFEQ L1
...
I can see that Intrinsics.areEqual does a null check. But I don't understand what happens with the call to java/lang/Boolean.valueOf when the reference is null
Boolean.valueOf is being called on the constant true in order to create a boxed Boolean object. This is then compared to the one passed to the function.
I know inline keyword means to avoid the call overhead calling a funtion. But I can't figure out what inline a extension property work for?
Let say we have two extension property named foo and another with is inlined named bar
val Any.foo : Long
get() = Date().time
inline val Any.bar : Long
get() = Date().time
Executing any of them, we gent the expected output, the current time.
The bytecode for this file is this below:
public final class InlinedExtensionPropertyKt {
public final static getFoo(Ljava/lang/Object;)J
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 9 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
INVOKEVIRTUAL java/util/Date.getTime ()J
LRETURN
L2
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
public final static getBar(Ljava/lang/Object;)J
#Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
L0
ALOAD 0
LDC "$receiver"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
L1
LINENUMBER 12 L1
NEW java/util/Date
DUP
INVOKESPECIAL java/util/Date.<init> ()V
INVOKEVIRTUAL java/util/Date.getTime ()J
LRETURN
L2
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
LOCALVARIABLE $i$f$getBar I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
#Lkotlin/Metadata;(mv={1, 1, 7}, bv={1, 0, 2}, k=2, d1={"\u0000\u000e\n\u0000\n\u0002\u0010\u0009\n\u0002\u0010\u0000\n\u0002\u0008\u0005\"\u0016\u0010\u0000\u001a\u00020\u0001*\u00020\u00028\u00c6\u0002\u00a2\u0006\u0006\u001a\u0004\u0008\u0003\u0010\u0004\"\u0015\u0010\u0005\u001a\u00020\u0001*\u00020\u00028F\u00a2\u0006\u0006\u001a\u0004\u0008\u0006\u0010\u0004\u00a8\u0006\u0007"}, d2={"bar", "", "", "getBar", "(Ljava/lang/Object;)J", "foo", "getFoo", "test sources for module app"})
// compiled from: InlinedExtensionPropertyKt.kt
}
We can see both are similar but differents only on these lines:
foo extract:
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
bar extract:
LOCALVARIABLE $receiver Ljava/lang/Object; L0 L2 0
LOCALVARIABLE $i$f$getBar I L0 L2 1
MAXSTACK = 2
MAXLOCALS = 2
I really don't understand what is happennig here.
Can someone point me to see what is the behaviour, or the equivalent in java, or some use for this?
Edit
Given the compiler will replace the content of inlined property, it may convenient to inline every extension property having not heavy operations ?
Thank you
From Kotlin's doc,
Note that, since extensions do not actually insert members into classes, there's no efficient way for an extension property to have a backing field.
and also,
The inline modifier can be used on accessors of properties that don't have a backing field.
As mentioned above, an inline extension property does not have a backing field. You may treat an extension property as a pair of static getter/setter, like this:
//In Kotlin
var Any.foo : Long
get() = Date().time
set(value) {
//Cannot access field here since extension property cannot have backing field
//Do something with `obj`
}
//In Java
public static long getFoo(Object obj) {
return new Date().getTime();
}
public static void setFoo(Object obj) {
//Do something with `obj`
}
So, inline property means that the code of the getter/setter function will be inlined into the call site when accessing the property (same as regular inline functions).
//In Kotlin
val x = "".foo
val y = "".bar
//Generated code
val x = InlinedExtensionPropertyKt.getFoo("")
val y = Date().time
For the bytecode that you post in the question, sorry that I am not able explain what is happening. But you may try to take a look at the bytecode of the following code:
fun get() {
val x = "".foo
val y = "".bar
}
, where "".foo will invoke the getter function but "".bar will not.
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?
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.
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.
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.