« If only he really had been | Main | Behind the scenes at the opening of the Berlin Wall »

October 31, 2009

Units of measure in F#

The latest version of F# introduces units of measure.  Using this feature, you can indicate that a numeric value is in a particular unit – metres, seconds, etc. – and the compiler will track that as part of the data type and validate that units are being converted and compared correctly.

For example, we can declare the following values:

let distance = 1000.0<m>
let time = 400.0<s>

Give these the ol’ Intellisense mouse waggle, and you find that distance has type float<m>, and time has type float<s>.  Try to add them together, and you get a tasty compiler error:

let nonsense = distance + time

FS0043: The unit of measure 's' does not match the unit of measure 'm'

But divide them and the compiler recognises that it makes sense, and figures out and tracks the derived unit of measure:

let speed = distance / time

speed has type float<m/s>, and again the compiler enforces that operations on speed are compatibly typed:

let description =
  if speed > 10.0<m s^-1> then
    "fast"
  else
    "slow"

Notice that F# supports the Proper Science notation of negative exponents as well as the Way Everybody Really Does It of division notation.

Derived units of measure can have names: the type float<kg m s^-2> is the same as float<N> and values of these types are therefore compatible.

let mass = 1.0<kg>
let accel = 50.0<m/s^2>
let whompiness = mass * accel

let ouch = whompiness > 100.0<N>  // compiles

Where do units of measure come from?  Well, when a daddy unit of measure and a mommy unit of measure love each other very much… but we’ve already talked about derived units of measure.  In fact, there are no units of measure built into the F# language or core library.  So either you have to define the units you need using the MeasureAttribute:

[<Measure>] type m

or you can import a bunch of standard SI units (basic units like the metre, kilogram and second, and derived units like newtons) from the F# Power Pack DLL:

// after referencing FSharp.PowerPack.dll
open Microsoft.FSharp.Math.SI

The Power Pack also includes a bunch of physical constants:

open Microsoft.FSharp.Math

let description =
  if speed < PhysicalConstants.c then
    "it's science"
  else
    "it's science... fiction!"

Naturally, these are all declared with the correct units of measure so efforts to compare Planck’s constant to the speed of light will meet with ignominious failure.  To work around this, tell your boss you need $370bn for a new particle accelerator.

To be clear, F# doesn’t assign any actual semantics to units.  For example, if you declare imperial units (because if it was good enough for the Babbage Engine, it’s good enough for the CLR, by Jove):

[<Measure>] type ft

F# won’t let you combine feet with metres.  It has no way of knowing that ft is a length measure, or how it would convert to metres.  However you can of course create conversion functions for your units:

let feetToMetres(f : float<ft>) =
  f * 0.3048<m> / 1.0<ft>
let totalLength =
  100.0<m> + feetToMetres(100.0<ft>)

Notice that our metres-per-foot expression spells out 1.0<ft> rather than 1<ft>.  F# only allows units of measure to be applied to floating point values, not integers.

Finally, be aware that units of measure are erased during compilation.  At runtime, F# (or rather the CLR) sees only plain old floats.  Unit checking is part of the compile phase only.

October 31, 2009 in Software | Permalink

TrackBack

TrackBack URL for this entry:
https://www.typepad.com/services/trackback/6a00d8341c5c9b53ef0120a6421005970b

Listed below are links to weblogs that reference Units of measure in F#:

Comments

Major coolness! (In an utterly geeky way of course.) I had the idea of building units of measure into a language, back around 1984, but never got around to fully creating a language to incorporate it, or hacking up an existing compiler to add it, or whatever.

As a step towards it, I created a Units of Measure library for C++ in 2003 (see http://davearonson.x10hosting.com/davearonson.com/meascpp/measure.html), but again never got around to porting it to anything more modern. Fortress (see http://en.wikipedia.org/wiki/Fortress_%28programming_language%29) was apparently originally supposed to have it, but that feature has been delayed indefinitely.

If I can spare the time, I intend to extend my version by allowing new units to be not only combinations, but *scalings* of prior units, so that for instance you could define "inch" as 0.0254 meters, and thus be able to use both inches and meters together -- plus feet, after defining that as 12 inches, yards, after defining that as 3 feet, and so on.

After that would be *additive* scalings like between degrees C and K. Once I have that in place, with both additive and multiplicative (if that wasn't a word before, it is now!) scalings available, I can easily define degrees F in terms of degrees C or K, or vice-versa.

Posted by: Dave Aronson at Nov 29, 2011 7:26:14 AM

Hi Dave,

Yes, whenever I talk about F# units of measure, one of the first questions that comes up is, "ooh, so if I add inches to kilometres, it converts between the two, right?" And I have to confess that it doesn't, that you have to insert a conversion at each point of use, rather than at the point of definition.

So I tend to think of F# units of measure as being more about sanity checking (a la dimensional analysis) than about interoperating different measurement systems. If you want to be sure you don't add a velocity to an acceleration, it's fine; if you want to handle both inches and centimetres, it's a bit more work.

Posted by: Ivan at Nov 29, 2011 9:02:29 AM