F Sharp Programming/Units of Measure

Units of measure allow programmers to annotate floats and integers with statically-typed unit metadata. This can be handy when writing programs which manipulate floats and integers representing specific units of measure, such as kilograms, pounds, meters, newtons, pascals, etc. F# will verify that units are used in places where the programmer intended. For example, the F# compiler will throw an error if a  is used where it expects a.

Statically Checked Type Conversions
Units of measure are invaluable to programmers who work in scientific research, they add an extra layer of protection to guard against conversion related errors. To cite a famous case study, NASA's $125 million Mars Climate Orbiter project ended in failure when the orbiter dipped 90 km closer to Mars than originally intended, causing it to tear apart and disintegrate spectacularly in the Mars atmosphere. A post mortem analysis narrowed down the root cause of the problem to a conversion error in the orbiter's propulsion systems used to lower the spacecraft into orbit: NASA passed data to the systems in metric units, but the software expected data in Imperial units. Although there were many contributing project-management errors which resulted in the failed mission, this software bug in particular could have been prevented if the software engineers had used a type-system powerful enough to detect unit-related errors.

Decorating Data With Contextual Information
In an article Making Code Look Wrong, Joel Spolsky describes a scenario in which, during the design of Microsoft Word and Excel, programmers at Microsoft were required to track the position of objects on a page using two non-interchangeable coordinate systems:


 * In WYSIWYG word processing, you have scrollable windows, so every coordinate has to be interpreted as either relative to the window or relative to the page, and that makes a big difference, and keeping them straight is pretty important. [...]
 * The compiler won’t help you if you assign one to the other and Intellisense won’t tell you bupkis. But they are semantically different; they need to be interpreted differently and treated differently and some kind of conversion function will need to be called if you assign one to the other or you will have a runtime bug. If you’re lucky. [...]
 * In Excel’s source code you see a lot of  and   and when you see those you know that they refer to rows and columns. Yep, they’re both integers, but it never makes sense to assign between them. In Word, I'm told, you see a lot of   and , where   means “horizontal coordinates relative to the layout” and   means “horizontal coordinates relative to the window.” Both ints. Not interchangeable. In both apps you see a lot of   meaning “count of bytes.” Yep, it’s an int again, but you know so much more about it just by looking at the variable name. It’s a count of bytes: a buffer size. And if you see  , well, blow the Bad Code Whistle, that is obviously wrong code, because even though   and   are both integers, it’s completely crazy to set a horizontal offset in pixels to a count of bytes.

In short, Microsoft depends on coding conventions to encode contextual data about a variable, and they depend on code reviews to enforce correct usage of a variable from its context. This works in practice, but its still possible for incorrect code to work its way it the product without the bug being detected for months.

If Microsoft were using a language with units of measure, they could have defined their own,  ,  ,  , and   units of measure so that an assignment of the form   not only fails visual inspection, it doesn't even compile.

Defining Units
New units of measure are defined using the  attribute:

Additionally, we can define types measures which are derived from existing measures as well:


 * Important: Units of measure look like a data type, but they aren't. .NET's type system does not support the behaviors that units of measure have, such as being able to square, divide, or raise datatypes to powers. This functionality is provided by the F# static type checker at compile time, but units are erased from compiled code. Consequently, it is not possible to determine value's unit at runtime.

We can create instances of float and integer data which represent these units using the same notation we use with generics:

Notice the that F# automatically derives a new unit,, for the value. Units of measure will multiply, divide, and cancel as needed depending on how they are used. Using these properties, it's very easy to convert between two units:

Units of measure are statically checked at compile time for proper usage. For example, if we use a measure where it isn't expected, we get a compilation error:

Units can be defined for integral types too:

Dimensionless Values
A value without a unit is dimensionless. Dimensionless values are represented implicitly by writing them out without units (i.e.,  ,  ), or they can be represented explicitly using the   type (i.e.  ,  ,  ).

We can convert dimensionless units to a specific measure by multiplying by. We can convert a measure back to a dimensionless unit by passing it to the built-in  or   methods:

Alternatively, its often easier (and safer) to divide away unneeded units:

Generalizing Units of Measure
Since measures and dimensionless values are (or appear to be) generic types, we can write functions which operate on both transparently:

Since units are erased from compiled code, they are not considered a real data type, so they can't be used directly as a type parameter in generic functions and classes. For example, the following code will not compile:

F# does not infer that  is a unit of measure above, possibly because the following code appears correct, but it can be used in non-sensical ways:

The type  can be a unit of measure or a data type, but not both at the same time. F#'s type checker assumes  is a type parameter unless otherwise specified. We can use the  attribute to change the   to a unit of measure:

F# PowerPack
The F# PowerPack (FSharp.PowerPack.dll) includes a number of predefined units of measure for scientific applications. These are available in the following modules:


 * Microsoft.FSharp.Math.SI - a variety of predefined measures in the International System of Units (SI).
 * Microsoft.FSharp.Math.PhysicalConstants - Fundamental physical constants with units of measure.

External Resources

 * Andrew Kennedy's 4-part tutorial on units of measure:
 * Part 1: Introducing Units
 * Part 2: Unit Conversions
 * Part 3: Generic Units
 * Part 4: Parameterized Types
 * F# Units of Measure (MSDN)