Design for dimensional analysis library with minimum overhead


I’m a part of a FIRST Robotics Team using Kotlin to program our robot. The computational resources that are available in this environment are nowhere near the level that modern computers have, and I’d like some advice with. For our 2019 season, our team, and several others, used a compile-time units wrapper that allowed for typesafe dimensional analysis throughout our code (Adding together different quantities was disallowed at compile time, rather than producing garbage results at runtime, for example). One of the problems that was faced is the overhead caused by instantiating large amounts of objects that are only in use for a little bit of time. The team that originally created this system ended up dialing back how much the wrappers were used because of the performance overhead they were seeing. As a part of that I’ve been trying to find a way to make this better, however all my attempts have been dead ends.

The key issue that I’ve found with this is that the wrapper classes only have to exist at compile time. Once compiled the wrappers are useless, as the code had to be validated through our rules during type checking. As such my first idea was to try to use inline classes, but that quickly hit a wall due to garbage bytecode that was produced for certain operations. That is disregarding the fact that inline classes keep a wrapper around just in case, and I have no control over whether the unboxed or boxed type is used.

Once typechecked, the only thing that the library we’re using does is provide ways to convert between units, which is just division between the contained Double and some other constant, so in a perfect world, the types would be able to be completely erased and replaced with doubles and some mul/div operations to convert between units. However I can’t think of a way to do that in Kotlin as it is today

Is there anything coming in the future that would allow that? Is this something that a compiler plugin would solve? I love Kotlin but in niche usecases like this, I feel as though it does come up a bit short

Inline classes are the good way to treat the problem. You just need to remember that as soon as you use inline in generic (for example, put it in list), it automatically generates wrapper. Metadata in the bytecode could be stripped later if you want. Another way is generics. They are erased in runtime and would not produce bytecode overhead. For example you can use a single class like Quantity<CM>(val value: Double) where object CM is a marker class that could be stripped after compilation. You can than limit operations on those classes by introducing them this way:

operator fun <T> Quantity<T>.plus(other: Quantity<T>) = 
    Quantitiy<T>(this.value + other.value)

I am not sure it helps, but in kmath, I developed a way to use singleton object as a context for operations instead of wrappers. For example, this class adds support for matrix operations on 2D structures without creating wrappers (usually you have single instance of such context for all matrices of the same type) and this class allows to interpret 1D arrays as 2D arrays inside the context (you need to have one for each set of dimensions). It probably would be a syntactic overkill for units.

A really nasty alternative option would you use multiplatform. You create a common library with two actual implementations (both targeting jvm). They both have expect types, but the “production” version would merely use type aliases, where the development/test version would have full types. As multiplatform compiles common code separately per platform this should work correctly (even though it isn’t really what it wasn’t designed for).