Units of measure

upd. We used to have type macros in v1 of macro paradise, and people have used them to implement units of measure, however since then type macros have been removed from macro paradise. It seems to me that the required functionality can be built on top of def macros and type-level computations, but I haven't looked into that yet.

One of the interesting requests in the comments to my recent post was units of measure for Scala. Units of measure represent an augmentation of the type system of F# (you might wish up to read a blog series for more details: first post, second post, third post, final post).

Despite the fact that compile-time metaprogramming lies on a different (lower!) level than type systems, this very use case can be implemented with macros. This brings an interesting question of when macros are appropriate, and when it's better to do away with them in favor of other language mechanisms. We will elaborate on this topic in future installations.

In the following example F# compiler is capable of inferring the unit of measure for a derived value speedOfImpact (the dimensionality will be m/s):

let gravityOnEarth = 9.81<m/s^2>
let heightOfMyOfficeWindow = 3.5<m>
let speedOfImpact = sqrt(2.0 * gravityOnEarth * heightOfMyOfficeWindow)

With macro types it becomes possible to write the code like that:

val gravityOnEarth = u(9.81, "m/s^2")
val heightOfMyOfficeWindow = u(3.5, "m")
val speedOfImpact = sqrt(2.0 * gravityOnEarth * heightOfMyOfficeWindow)

u is a macro def that takes a number and wraps it in a macro type that depends on the unit of measure passed. That macro type hosts all necessary stuff that overloads relevant operations (arithmetic, math._ stuff, so on). These operations upon units of measure would themselves be macros that produce new types as necessary.

An alternative approach would be to implement u as a macro annotation that would operate on expression-level and ascribe numeric values with units of measure. Such a macro would be able to do everything a macro def can do, but it would look in, arguably, a nicer way:

val gravityOnEarth = 9.81: @u("m/s^2")
val heightOfMyOfficeWindow = 3.5: @u("m")
val speedOfImpact = sqrt(2.0 * gravityOnEarth * heightOfMyOfficeWindow)

At a glance, this looks like a workable solution, but, of course, it will be possible to fully assess the feasibility of this idea only when we have an implementation of the macro system that can be used for tests.