Primitive Type Specialization


#1

Has Scala-like type specialization (or something similar) been considered for implementation?
In a nutshell:


class Matrix<@Specialize("Float", "Int") T : Number>(val width: Int, val height: Int) {
  private val storage = Array<T>(width * height) { 0 }

  operator fun get(row: Int, column: Int) = storage[row * width + column]
  operator fun set(row: Int, column: Int, value: T) = storage[row * width + column] = value
}

would define the classes FloatMatrix and IntMatrix with the generic Array<T> specialized to FloatArray and IntArray, respectively. Any of these classes already existing would obviously result in an error.
One could even consider primitive arrays ( IntArray, FloatArray etc.) as being specialized versions of Array<T>.
Edit: the goal of specialization is to eliminate boxing/unbixing overhead.


#2

I don’t think there are enough primitives to make this that much worth a language feature instead of just making an inline class. E.g. a UByteArray can be considered a specialized version of ByteArray. So, yes, you’d have to repeat each like inline class FloatMatrix(val arr: FloatArray) but you can use differing levels of abstraction to help with some reuse.


#3

I can’t see how inline classes are related to this. Specialization also applies to classes with more than a single field, obviously, and possibly multiple type parameters.


#4

:frowning: It’s not obvious, the first example in your only link obviously shows only a single field. In fact, many uses of specialization do only have a single field and can therefore solve the performance problem w/ inline classes. Specialization only has value for languages that differentiate between boxed and unboxed versions of primitives which Kotlin does not really (and it only matters on one of Kotlin’s target platforms). Due to how Kotlin uses boxed primitives for that one platform in most places (e.g. there is no IntList), the language designers don’t believe the performance cost is that large. The reason I bring up inline classes is because you can conform to a common abstraction without the indirection overhead in many cases.

If this did have enough value, I’d prefer to see a more configurable metaprogramming system, ala macros, than just specialization for the few platform-specific primitive types on only one of the three platforms it matters on.


#5

Yeah, your right about my example. Have you read the Scala spec linked in the original post? The cost of boxing is not negligible for many application (e.g. scientific computing or number crunching). A matrix class is a case in point here, that’s why it was chosen. There is a reason, after all, why we have IntArray and not just use Array<Int> everywhere and why the Scala people came up with a solution to this problem.
Inline classes do not help at all here. The only alternative, apart from copying and pasting code, would be to use Array<T> and burn a lot of CPU cycles (and memory) on unboxing/boxing.
I don’t know how generics are implemented in Kotlin/Native and if boxing can be optimized out of Array<Int> there, possibly…


#6

Boxing is a problem for high-performance computations. I like the idea to generate additional specialized classes for primitives. It is very close to the idea of inline classes, so it should not be hard to do.

I also like the syntax since it does not involve new language constructs, just an annotation that could be ignored if the compiler does not support it. The only change I would propose is to use enum constants instead of strings.

Would you consider to compose a KEEP PR for that? If you want to discuss it, there is a slack channel for mathematics now.


#7

Ok, great. I’ll jot something down.