measured
measured copied to clipboard
Compile-time units resolution
Use compile-time units resolution in order to avoid number boxing.
The basic idea is the following:
- Make
Measurement
an interface. And keep the implementation you already have for dynamic units. - Add an inline
Measurement
implementation like this:
inline class InlineMeasurement(<U: Units>(val value: Double): Measurement<U>
- Add a resolver:
inline fun <reified U:Units> resolveUnits(): Units
and use it to loadUnits
object when it is needed. The resolver could work in different ways. The simplest way is to loadobjectInstance
from reflects.
Both implementations could be used in different cases: boxing one in cases, when the Units are dynamic, inline when you can infer everything in compile-time.
This might be achievable by making Measure
itself inline
. Doing so would remove its ability to track the current unit, so its value would be in the "base" unit. This means:
val a = 60 * minutes
// a == Measure(60 * 60 * 1000), since minutes.ratio == 60000
All operations would be kept in the base unit:
val speed = 60 * miles / hours
// speed would be 0.0268224 m/ms (60 * 1609.344 / 3600000)
// Measured default Time unit is milliseconds
Everything else should work as it does now, with the exception of Measure.
as(Unit)
and Measure.toString()
. The former wouldn't make sense anymore since Measure
would never be in anything but base units. The toString
behavior would need a new method; something like:
inline class Measure<T: Units>(val value: Double) {
//...
fun display(with: Units): String {...}
//...
}
Unfortunately, inline
classes are boxed whenever they are consumed as an interface they implement. So adding one would nullify the value of this change.
However, a major downside of exposing an inline
class in the API is it requires consumers to have inline
classes as part of their function signatures. Which prevents Java from calling them due to name mangling.