Kotlin, targeting Java interop: Idiomatic type for lazy collection? - kotlin

When targeting Java interop, what type should one use for a lazy collection?
Sequence<T> makes the most sense for Kotlin callers due to extension functions on it being lazy by default, but forces Java callers to deal with a Kotlin stdlib type and convert the sequence iterator manually (sequence does not extend iterable!)
Iterable<T> makes sense for Java callers due to implicit usage in for loops, but will cause unsuspecting Kotlin callers to accidentally drop laziness due to non-lazy extension functions
Stream<T> is optimal for both Java and Kotlin callers but may have overhead and is Java 8+ (Kotlin targets 6+)

You can make everyone happy by implementing all three. e.g.:
data class User(val name: String)
fun userSequence(): Sequence<User> = TODO()
fun usersLazily(): Iterable<User> = userSequence().asIterable()
fun userStream(): Stream<User> = userSequence().asStream()
By not using a simple name like users for any of these functions you make the caller think just a little extra on which one they really want:
Kotlin users will use userSequence.
Java 1.6 and 1.7 users will use usersLazily.
Java 1.8 users will use userStream.
userStream will have to be defined in a separate JAR adding JDK 1.8 support to your 1.6/1.7 JAR (similar to how org.jetbrains.kotlin:kotlin-stdlib-jre8 does for org.jetbrains.kotlin:kotlin-stdlib).
With that said, I would question whether you really need to maintain Java 1.6 or 1.7 support. If you find you don't then you can put userSequence and userStream into the same JAR and not even define usersLazily.

Related

Is the jvm method name of function with inline classes stable?

I declared an inline class
#JvmInline
value class Creator<T>(val type: KClass<T>);
, and declared an interface
interface Itf {
fun <T> creator(type: KClass<T>): Creator<T>;
}
I want to implement this interface by generating the bytecode by asm(https://asm.ow2.io/ 1).
I found java method decompiled from bytecode is
public KClass<T> creator-9k1ZQyY();
The java method name is “creator-9k1ZQyY”. the suffix “-9k1ZQyY” is added by kotlin compiler and I know why kotlin compiler did it.
This suffix is very important for bytecode generator.
My question:
If the interface and inline class are stable, can kotlin compiler guarantee that suffix is stable too? Does that suffix have nothing to do with the version of kotlin-compiler?
The docs seem to suggest the mangling is stable:
functions using inline classes are mangled by adding some stable hashcode to the function name
As noted in the same doc, the mangling scheme has changed once with the version 1.4.30 of the Kotlin compiler, but I would consider it quite stable nonetheless. They even provided a flag to use the old scheme to generate binary compatible code, so I'm assuming it's not only unlikely to change again, but even if it does, it will surely be done with some way to keep compatibility.

Why do we need an explicit function interface modifier in Kotlin?

consider a SAM defined in Java
public interface Transform {
public String apply(String str);
}
This interface supports lambda to type conversion in Kotlin automatically
fun run(transform: Transform) {
println(transform.apply("world"))
}
run { x -> "Hello $x!!" } // runs fine without any issues
But now consider a Kotlin interface
interface Transform2 {
fun apply(str: String): String
}
Now the only way to invoke the run function would be by creating an anonymous instance of Transform2
run(object : Transform2 {
override fun transform(str: String): String = "hello $str!!"
})
but if we make the Transform2 interface a functional interface then the below is possible
run { str -> "hello $str!!" }
Why the Kotlin compiler cannot automatically type cast lambdas to matching interfaces (just as it does with Java interfaces) without needing to explicitly mark the said interfaces as a functional interface.
I've found some kind of a rationale in a comment in KT-7770:
... treating all the applicable interfaces as SAM might be too
unexpected/implicit: one having a SAM-applicable interface may not
assume that it will be used for SAM-conversions. Thus, adding another
method to the interface becomes more painful since it might require
changing syntax on the call sites (e.g. transforming callable
reference to object literal).
Because of it, current vision is adding some kind of modifier for
interfaces that when being applied:
Adds a check that the interface is a valid SAM
Allows SAM-conversions on call sites for it
Something like this:
fun interface MyRunnable {
fun run()
}
Basically, he is saying that if the SAM conversion were done implicitly by default, and I add some new methods to the interface, the SAM conversions would no longer be performed, and every place that used the conversion needs to be changed. The word "fun" is there to tell the compiler to check that the interface indeed has only one abstract method, and also to tell the call site that this is indeed a SAM interface, and they can expect the author to not suddenly add new abstract methods to the interface, suddenly breaking their code.
The thread goes on to discuss why can't the same argument can't be applied to Java, and the reason essentially boils down to "Java is not Kotlin".
This is speculation, but I strongly suspect one reason is to avoid encouraging the use of functional interfaces over Kotlin's more natural approach.
Functional interfaces are Java's solution to the problem of adding lambdas to the Java language in a way that involved the least change and risk, and the greatest compatibility with what had been best practice in the nearly 20 years that Java had existed without them: the use of anonymous classes implementing named interfaces. It needs umpteen different named interfaces such as Supplier, BiFunction, DoublePredicate… each with their own method and parameter names, each incompatible with all the others — and with all the other interfaces people have developed over the years. (For example, Java has a whole host of interfaces that are effectively one-parameter functions — Function, UnaryOperator, Consumer, Predicate, ActionListener, AWTEventListener… — but are all unrelated and incompatible.) And all this is to make up for the fact that Java doesn't have first-class functions.
Kotlin has first-class functions, which are a much more general, more elegant, and more powerful approach. (For example, you can write a lambda (or function, or function literal) taking a single parameter, and use it anywhere that you need a function taking a single parameter, without worrying about its exact interface. You don't have to choose between similar-looking interfaces, or write your own if there isn't one. And there are none of the hidden gotchas that occur when Java can't infer the correct interface type.) All the standard library uses function types, as does most other Kotlin code people write. And because they're so widely-used, they're widely supported: as part of the Kotlin ecosystem, everyone benefits.
So Kotlin supports functional interfaces mainly for compatibility with Java. Compared to first-class functions, they're basically a hack. A very ingenious and elegant hack, and arguably a necessary one given how important backward compatibility is to the Java platform — but a hack nonetheless. And so I suspect that JetBrains want to encourage people to use function types in preference to them where possible.
In Kotlin, you have to explicitly request features which improve Java compatibility but can lead to worse Kotlin code (such as #JvmStatic for static methods, or casting to java.lang.Object in order to call wait()/notify()). So it fits into the same pattern that you also have to explicitly request a functional interface (by using fun interface).
(See also my previous answer on the subject.)

Are Kotlin lambdas the same as Java lambdas under the hood?

This is a follow up question of this answer.
But when the application hasn’t used lambda expressions before¹, even
the framework for generating the lambda classes has to be loaded
(Oracle’s current implementation uses ASM under the hood). This is the
actual cause of the slowdown, loading and initialization of a dozen
internally used classes, not the lambda expression itself
Ok, Java uses ASM to generate the classes on runtime. I found this and if I understood correctly, it is basically saying that Kotlin lambdas are compiled to pre-existing anonymous classes being loaded at runtime (instead of generated).
If I'm correct, Kotlin lambdas aren't the same thing as Java and shouldn't have the same performance impact. Can someone confirm?
Of course, Kotlin has built-in support for inlining lambdas, where Java doesn't. So many lambdas in Kotlin code don't correspond to any objects at runtime at all.
But for those that can't be inlined, yes, according to https://medium.com/#christian.c.carroll/exploring-kotlin-lambda-bytecode-8c2d15afd490 the anonymous class translation seems to be always used. Unfortunately the post doesn't specify the Kotlin version (1.3.30 was the latest available at that time).
I would also consider this an implementation detail which could change depending on Kotlin version at least when jvmTarget is set to "1.8" or greater; so there is no substitute to actually checking your own bytecode.

Concurrent Hash Map in Kotlin

Is it possible to implement a concurrent hash map purely in Kotlin (without Java dependency)? I am new to Kotlin and it looks like there is no obvious API available in kotlin.collections.
You can probably convert the source without too many issues. It's freely available, here for example. The concurrency model of Kotlin multiplatform (which I'm guessing is your goal, there's no point in reimplementing it if you only target the JVM) is a bit different than the one Java uses, there are no locks for example. But there's no reason why that would prevent it.
The following resources might also help you with the implementation:
Concurrency in Kotlin/Native
kotlin.native.concurrent package
Replacement for synchronized
Official Kotlin/Native Concurrency tutorial
You can try:
val emitters: ConcurrentMap<String, Any> = ConcurrentHashMap()
// get
val obj: Any = emitters[email]
// put:
emitters[email] = this
// delete
emitters.remove(email)
Such way, u don't need to add any library to your project

Kotlin: print class name without reflection in multiplatform project

Data classes print out just fine in MPP projects. When I toString() the KClass object for my class, I get:
class com.example.MySimpleClass (Kotlin reflection is not available)
How Can I do what data class does and have a nice clean name without reflection?
I don't have it set up myself to test, so answer is based purely on documentation:
KClass.simpleName is available in Common code; qualifiedName isn't, but since 1.3 it is on every platform, so you could define an expect fun in your multiplatform part and make all actual implementations access qualifiedName.