Preprocessor to avoid code-duplication for primitive type specialization

Generics do not exist for primitive types in Java or Kotlin and specialization for multiple/all primitive types results in a lot of code duplication. For example,

fun IntArray.bubbleSort() { ... }
fun FloatArray.bubbleSort() { ... }

would use the exact same code except for the primitive type name. Doing this for multiple/all primitive types is very cumbersome. As far as I understand, pre-processors exist for Java that help reduce code duplication in such a scenario, e.g. JPSG. Is there a Kotlin preprocessor that does something similar?

This is probably related to Primitive Type Specialization

It is not that simple. It requires generation of multiple byte-code blocks. In the open world, generating code for all primitives could significantly increase byte-code size.

Some discussion would be good. Please also see that issue and related ones.

Stcitly speaking generics do exist for primitive types (they just get boxed) Kotlin doesn’t even have primitive types per se (Kotlin types might get mapped to Java’s primitive types depending on the use case).

Also, the only case where having the dedicated “specialization” is when arrays are involved as those are way more efficinent on the JVM.

I probably do not understand the exact compilation process well enough so my intuition may be incorrect. I had envisioned a pre-processor that would run before the Kotlin compilation and would create Kotlin code (and not byte code) for primitive types specified by the user (not necessarily all primitive types) that then gets compiled like regular Kotlin code. That should not create more byte code than explicitly copy & pasting the implementations by hand without a pre-processor. What am I missing?

True but I do not think that that is an argument against some sort of generics for “primitive” types in Kotlin because a common use cases is Kotlin as JVM compatible replacement of Java.

This is exactly how C++ pre-processor or Julia JIT works. The problem is that if you have, say fun <T: Number> Array<T>.doSomething(). You do not know in advance, what specific specialization you should generate the code for. It is possible you will need all of them, it is also possible that you will need only one or two. Automatic code generation for all will be really a waste. So in order to make it work, you need either scala-like specialization annotation, or something more smart, like for example inline specialization: you define your function as inline and it is being specialized in use-place.

I think that something like that will be added in future, but for now some it requires a lot more thinking. I also think that it will be quite easy to generate scala-like specialization with compiler plugins.

3 Likes

Thanks, that clarified it a lot. My idea was something like the scala-like specialization annotation that you mention and is also discussed in Primitive Type Specialization. I understand that to implement it as an official language feature, more thought will have to go into it. A fairly naive compiler plugin that generates only specializations by copy&replacing with primitive types as specified by the programmer (and not by the downstream caller) would definitely satisfy my own needs.

Update: Is there a way to mark a post as solution for the thread? I have seen it in other discourse forums but I cannot find it here.

There is a better approach than a preprocessor. The C preprocessor is very limited in that it has no language level existence. This problem could be better supported by templates or compile time metaprogramming. The latter is more powerful, but not very well-explored. It also fundamentally changes the nature of a language. In some ways the Kotlin variant of it is compiler plugins, but that is not finalised and would probably be much more heavyweight in syntax (writing the plugin).

2 Likes

I do like the idea of templates/meta programming and if Kotlin could bring something similar to C++ template/meta programming to the JVM world, that would be a really powerful tool.

This is exactly what I was talking about. Compile plugin still adds another phase to the compilation, so it is not an ultimate tool. Some things could be done by simple optimizations without changes to language.

Templates are a complicated thing and I am not sure that it is the best approach. C++ version of templates in some cases do more evil than good. They break tooling, generate problems with the type safety and make C++ almost impossible to interop with. As far as I understand, Rust made some progress in that direction, but there are not ultimate solutions yet.

1 Like

Certainly, Interop on the JVM may be easier (as it is not as ABI sensitive). A big problem with C++ templates is that they have no types (yet) and can get abused for compile time metaprogramming. Unlike proper meta programming, a type template instantiation is not a normal type which causes interop issues. Part of this is due to the general issues OO has with interop. But templates as C++ does them are certainly not the direction to go. Any approach would need a lot of though of how to do it well (except for compiler optimizations of course).

1 Like