Units of Measure


#1

With my engineering background, I am fascinated by the support for units of measure on a language level in F#. (As can be seen in this research paper: http://typesatwork.imm.dtu.dk/material/TaW_Paper_TypesAtWork_Kennedy.pdf.)

I found this example by someone who tried to build the feature in the language:

Would it be possible to achieve something similar as part of the type system in Kotlin as F# does?


#2

In fact it is quite easy. You just need to extend the Number class like:

infix fun Number.of(unit: Unit): Number{
  return this*unit.multiplier
}

where multiplier is the ratio between given unit and some standard unit
or

infix fun Number.of(unit: Unit): Value{
  return Value(this, unit)
}

where Value stores both numeric value and units. Of course, in the last case you well need also to implement mathematical operations for your Value class and/or inherit it from Number.

the call will look like this:

cm = Unit(...) // somewhere in constants declaration
12 of cm // where you need it

#3

You can also use an extension property:

val Number.cm get() = Value(this, cm)

You may want to override the various number primitives rather than the number box, but that is a separate matter. You would use this as:

val length = 5.cm
val width = 3.4.cm

#4

Nice. The magic has to happen in Unit. Citing page 18 of the linked document:

For languages such as Pascal and Java that support only type checking, it is
relatively easy to add support for checking units-of-measure, using the procedure
outlined in the previous section. But for F#, we go further, and support full type
inference. To do this, we must not only check equations; we must solve them.

…which means that when, for example, multiplying two numbers with the unit meter (m) you would get square meter (m*m, or m^2).
But I am afraid this can only be caught during runtime, not compile time.


#5

You can do it in compile time with complicated generics like Value<T : Unit>. But I think it does not worth the time spent since in any case you will work with some user input that could not be checked in compile time.


#6

It is very hard to do this compile safe with generics in the case of derived units such as m/s or m/s^2. Even in C++ with full templates it is very hard. The best solution would be to do it with manual overloads/extension functions and not allow division/multiplication of multi-unit values otherwise.


#7

A couple of articles

http://javanut.net/2017/05/23/more-fun-with-generics-in-kotlin/


#8

The struggle in jvm based languages with a units system is balancing the desire to have compile units checking (I can’t assign a length to a variable meant to hold time) vs. wanting to keep it lightweight (not having every value be a full blown object on the heap). There is no way to have both in Java 9 and this can only be solved with value objects which are part of Project Valhalla.

One thing to be careful of when defining a type-safe units system is that you want to focus on what the quantity is meant to represent not the units used for the quantity. So Length and Time are different quantities and thus different types. Centimeters and Inches are different ways to create and extract a Length quantity and are not type incompatible. This is analagous to the way a date in Java is an instant in time. There are not different date types for each time zone.


#9

I think it’s more than just that, even if you’re willing to create objects for quantities that associate a number with a unit the generics get really complicated really fast with derived units of derived units of derived units and so on. There is a JSR to add units to the JVM, it creates objects and cannot infer units at compile time. I think the only reason is the impracticality of very complex generic types, I think this can be remedied with type aliases. Basically you only define base units and dimensions for example: Length and Time. Then speed is DerrivedDimension<Length, Time>, similar to the example in the second link @fvasco provided. And the only way you explicitly define speed is with a typealias Speed = DerrivedDimension<Length, Time>. However I do not think it is possible to do this in a way that is compatible with JSR-363 (not sure how important that is).

The plus side is I do think it is possible to make a clean implementation of a statically checked inferred units library for Kotlin using typealias and in a way that should work across all targets. Of course until value types are added there will be extraneous object creation. I made Kotlin extensions for uom-se and I’m currently using that as a units library, this was not very much work because most of the work is done in the underlying Java library. I would eventually like to make a Kotlin specific units library that is statically checked for inferred units and leverages typealias but I think this would be a ton of work to do well (if it is indeed possible to implement well at all) so hopefully someone else does this :slight_smile:


#10

I was just reading the following page: https://fsharpforfunandprofit.com/posts/units-of-measure/ and the last paragraph states:

Units of measure at runtime
An issue that you may run into is that units of measure are not part of the .NET type system.

F# does stores extra metadata about them in the assembly, but this metadata is only understood by F#.

This means that there is no (easy) way at runtime to determine what unit of measure a value has, nor any way to dynamically assign a unit of measure at runtime.

It also means that there is no way to expose units of measure as part of a public API to another .NET language (except other F# assemblies).

So, I thought that maybe the information about units of measure should not be entirely based on generics, instead utilizing annotations, or maybe it is possible to combine generics and annotations in some sensible way? Could annotation processors help making this work during compile time? I have never used them myself.

@Unit(meter / second^2)
val acceleration = 2.0

@Unit(second)
val time = 10.0

val speed = acceleration * time

speed.unit  // meter / second

@Unit(pascal)
val pressure = 10.0

speed + pressure  // Runtime unit error, ideally we'd like to check this at compile time (annotation processors?)

#11

What you are talking about is the pre-processor and a complex one.You can type check variables declarations (you can do it with generics as well), but what about expressions? You will either have to annotate any expression results by hand or perform full language analysis.
What I don’t understand is why do you need it? What advantages does static check have? If you want to check types without calculating expression then just make calculations lazy. It requires pretty little, just two-component object containing units and lambda-expression.


#12

Well, I wrote down some thoughts about types.

I also found that this book https://pragprog.com/book/swdddf/domain-modeling-made-functional captures the applications of my evolution of thoughts after having the opportunity to taste what is being done in Idris, Haskell, Scala, and so on.

The advantage of static checks is to reduce the number of tests. The IDE can give better advice. It also helps a lot when doing refactoring.


#13

Hello,
I’ve attempted to create such a thing for my robotics team.
It is able to represent arbitrary dimensional analysis in a type-safe manner.

https://github.com/kunalsheth/units-of-measure

Please tell me if you have any feedback.