In the second case (the one with create
), can you indicate how you figure out that the bytecode uses primitives ? I have been unable to reproduce this behavior. I can see an object with two invoke
functions, one taking primitives and the other taking boxed types ; I can get the code with decorate
behave the same way by massaging it to get it closer to the code using create
. But as I understand it, what will matter to you is whether the Doubles are boxed when the function is called, and since the object has both the boxed and unboxed version this falls short of exhibiting the behavior you want ? Importantly, note that because create
is declared as returning Any
, you cannot call the method on this object without downcasting, and I do not think you’ll be able to downcast it to any object that will use the primitive types (short of declaring the specialized type and using it everywhere – which would void the point of using generics).
So if I understand correctly, in Kotlin (A, B) -> C
is purely syntactic sugar for Function2<A, B, C>
. That means that as long as you write this return type explicitly, or taking this argument type explicitly, you are explicitly requesting the boxing behavior.
I do not know of any way to ask Kotlin to compile this code without defining a type somewhere, and at that point, either you define the specialized type (then you have to do it for each different primitive you want) or you define a type with generics (then you have boxing). Structural typing would probably be the modern way to write this with no explicit declarations, but Kotlin doesn’t have structural typing.
So all in all I don’t think this is possible today.
Note that for most applications, boxing is no concrete problem at all. I observe that most cases where programmers want to get rid of boxing is more out of some feeling of wastefulness at boxing rather than an actual performance need. Maybe in your case you actually need the additional performance, but I’d encourage you to consider whether this is not premature optimization ; consider especially how this looks relatively easy to fix later (when you measure that boxing is really an issue) at the cost of additional complexity by defining ad-hoc types with the methods taking the primitives for each combination you need :
interface Function2Double {
fun invoke(a : Double, b : Double) : Double
}
inline fun decorate_double(crossinline l : (a : Double, b : Double) -> Double) : Function2Double {
return object : Function2Double { override fun invoke(a : Double, b : Double) = l.invoke(a, b) }
}
decorate_double { a : Double, b : Double -> a + b } // doesn't box