Units of Measure

Yes, but that does not mean that you can use compiler plugins from java for kotlin. Normal java code works with kotlin, but compiler plugins for java and kotlin something completly different. You would need to write a completely different plugin for kotlin than for java.

I couldn’t find any. There is an issue on github about it, but it doesn’t seem like there is any work done. Also there isn’t even a stable or documented API for compiler plugins for kotlin so I’d be really surprised if there was anything done yet.

Nothing directly. But the OP only provided those links to explain what he wanted. Whenever someone else mentioned another solution from a programming language it was in combination with how this could be applied to kotlin, which you didn’t.
If you meant to say that one solution would be to change kotlin to allow for such expressions (e.g. by creating a compiler plugin) than I’m sorry that I didn’t understand you.

The compiler plugin is more about generating code. In this case one would need to amend the grammar. It is certainly possible to make a Kotlin derived language but that wouldn’t be Kotlin, nor is it, as far as I understand, the goal of Kotlin plugins to support doing that.

The javac plugin “API” is no more capable or documented than the kotlinc API. Java’s is designed so a plugin can listen to compiler before/after events for parsing, type attribution, and so forth (just as with annotation processing), which ain’t much, but it doesn’t take much. Really just about any hook into the compiler is enough to fully exploit it. It’s just a matter of “hacking” at that point. As you can imagine the Manifold plugin has to hook into and override parts of the compiler to achieve seamless integration, which of course is far outside the scope of intentions for the javac plugin API, but there it is. You can argue about whether or not Java is still Java in this context, but really, what does that matter? What does matter are the new capabilities offered by such a plugin, how useful they are to you, and the level of support available in your development environment.

Anyhow there is progress with the kotlinc Manifold “plugin”, but it’s slow going and limited to Manifold’s core tech, type manifolds – unit expressions and other language augmentations will never see the light of day in Kotlin. It’s a private repo (as is the Manifold Intellij repo), but that doesn’t mean they don’t exist.

What you need is actually a ‘unit type’ accompanying an expression.
That ‘unit type’ is composed of two lists of base types one for numerators and one for denominators.
In addition you have to support algebra of multiplication and division on those ‘unit types’.
and example for a multiplication:
meter/(secondsecond) X kgsecond → meter*kg/second
This unit type has to be checked for compatibility when performing operation or assignment.
For example: 1meter - 3seconds → ERROR

this can be achieved by defining a new type that holds a numeric value and the ‘unit type’.
Implement it’s operators for type checking and unit algebra.
Implement it’s setter for type checking.

Actually we’re doing something like that in a language for autonomous vehicle checking called M-SDL.

Actually if you are looking for something small and simple, you can checkout something I’ve built.
Search for “PhysicaUnits kotlin” in Github.
It allows you to define any raw unit combination, perform operations in them and combine them with real number algebra.
You can start from there and adept it to your needs.

I just released Measured. A library that makes units a lot simpler and intuitive to work with. It uses the compiler to enforce correctness and lets you combine units into more complex ones using math operators. It is also extensible, making it easy to define your own units.

val velocity     = 5 * meters / seconds
val acceleration = 9 * meters / (seconds * seconds)
val time         = 1 * minutes

//  d = vt + ½at²
val distance     = velocity * time + 1.0/2 * acceleration * time * time

println(distance                ) // 16500 m
println(distance `as` kilometers) // 16.5 km
println(distance `as` miles     ) // 10.25262467191601 mi

println(5 * miles / hours `as` meters / seconds) // 2.2352 m/s

Try it out and provide feedback.

7 Likes

Very nice library
Would it be possible to have addition (and get an Error when adding incompatible type) :

val velocity  = 5 * meters / seconds
val time = 1 * minutes

val undecidable = velocity + time // throw error (or better, doesn't compile)

and also, adding like

val time  = 1 * minutes
val sec = 30 * seconds
val total = time + sec // 90 seconds

Thanks for checking it out. The library already supports proper handling of addition as you mentioned. Please keep the feedback coming.

I discovered the existence of this in F# just recently, and wondered how I could get it in Kotlin.

I considered using a design like Measure<Unit> myself, but had issues with it after I tried to use it in something more complex. If you use large arrays/lists of values with units, you have to choose between two equally bad options - either store them as primitives and wrap them when the caller asks for one element, or store them as wrappers and take the extra memory hit. Either option eats into computation time, and in my ray tracer, the cost/benefit trade-off wasn’t too appealing.

The nice thing about how this works in F# is that, at runtime, the values are just primitives, so it shouldn’t perform any worse than an array of primitives with no unit information. And in the case of JVM at least, the vector API (still in incubation) is convenient only when working with primitive arrays (or perhaps buffers? I haven’t investigated) - if you have wrappers then you have to copy the values out into an array before passing them off, and the extra copying is enough overhead that it almost defeats the purpose of using the vector ops. (I know, because I used to store my doubles in an ImmutableList<Double>, and switched to a custom ImmutableDoubleArray for this exact reason.)

Anyway, you would think that inline classes would be one way to remove that overhead, sort of like how Compose’s Dp works.

I use a similar strategy for my Angle class because it was way too easy to screw up the units of angles when dealing with trigonometry. 8.degrees or 8.radians both return an Angle which internally is always just storing radians, and then I just have my own version of all the trig functions… that worked cleanly - until I wanted to store a Complex for an angle instead of a Double. I haven’t introduced ComplexAngle yet but it might happen… it won’t appear in public API so it isn’t as important.

The problem with trying to do inline classes for all types of measurement is that you then have expressions like 10.m / 5.s which has to return 2 m/s somehow… so you end up with a million little inline classes for all possible combinations and all the methods for the operators returning the right inline class for the resulting units. And eventually, someone hits a combination which there is no class for yet, because nobody imagined someone would want something like m/s³.

I don’t know if any library is trying to use inline classes for this yet - all the ones I’ve seen so far have been normal wrapper/data classes.

So I too think that type annotations are probably the way to go about this. I just have no idea how to implement that. Whatever it is, it would have to allow me to annotate Double, and DoubleArray, and anything other custom class that might also be a container of doubles (like my own ImmutableDoubleArray…)

Apologies for a bit of thread necromancy, but I thought it was better than creating a duplicate thread.

1 Like