# Units of Measure in F#: Part Two, Unit Conversions

In today's article I'll show you how to use different systems of units, convert between units, and interface with non-unit-aware code.

### PowerPack goodies

First, though, let's have a look at some handy definitions provided in the F# PowerPack. To access this DLL, you'll need to reference if from your project. If you're working in Visual Studio, right-click on References in the Solution Explorer window.

Now select FSharp.PowerPack and click OK.

If you're working in FSharp Interactive, type

Now you'll have at your disposal the complete set of SI base and derived units, defined Microsoft.FSharp.Math.SI.

Also available are various physical constants, defined in Microsoft.FSharp.Math.PhysicalConstants.

### Multiple unit systems

So for physicists, at least, there is no excuse to go non-metric. But what if you insist? No problem! Here is the example from Part One, using feet instead of metres as the unit of length.

What if you need to convert between feet and metres? First, define a conversion factor.

What are the units of feetPerMetre? Answer: feet per metre (doh!), or ft/m for short. Now we can convert distances...

...and speeds...

...and we can convert back the other way by multiplying instead of dividing:

As far as F# is concerned, ft and m have nothing to do with each other. It's up to you, the programmer, to define appropriate conversion factors. But the presence of units on the conversion factors makes mistakes much less likely. For example, what happens if I divide instead of multiply above? The type of the result suggests that something is awry, and will probably lead to a compile-time error later in the code:

It's probably a good idea to package up conversion factors with the unit-of-measure to which they relate. A convenient way to do this is to add a static member to the unit-of-measure "type":

Now we can just write ft.perMetre.

### Interfacing non-unit-aware code

In Part One, we saw how to use syntax such as 2.0<s> to introduce units-of-measure into the types of floating-point values. But what if a quantity is stored in a file, or entered by the user through a GUI, or in a web form? In that case it'll probably start out life as a string, to be parsed and converted into a float. How can we convert a vanilla float into, say, a float<s>? Easy: just multiply by 1.0<s>! Here's an example:

If we want to convert back to a vanilla float, say, to pass to a non-unit-aware .NET method, we just divide by 1.0<s>:

### Dimensionless quantities

But hang on a minute - what's going on with that last example? The variable timeInSeconds has type float<s>, and we divided it by 1.0<s> which has type float<s>. So the units cancel out, producing units which we write simply as the digit 1. Hence the type of timeInSeconds / 1.0<s> is float<1>. Such a quantity is called dimensionless. Conveniently, F# defines the ordinary float type to be an alias for float<1>,using the definition

type float = float<1>

which makes use of overloading on the arity (= number of parameters) of the type. (Overloading is used to similar good effect with the non-generic .NET type System.Collections.IEnumerable and its generic variant System.Collections.Generic.IEnumerable).

Summing up, we've learnt about unit conversions of various kinds, between different unit systems, and between ordinary floats and floats-with-units.

Next time we'll look at generic unit types, or: what is a good type for fun x -> x*x?

Tags

1. alexey_r says:

A great feature!

How do unitful quantities look to the CLR? Something like

[Measure]

struct Second : IMeasure {}

and

[Measure]

struct Mult<M1,M2> : IMeasure where M1 : IMeasure where M2 : IMeasure {}

?

2. wil2200 says:

Great post. However, I am having issues running the "gravitionalForce" portion of it.

I keep getting an error saying that "error FS0039: The namespace or module ‘SI’ is not defined." even though I referenced it in both ways (I also have powerpack added to the project):

open SI and/or

open Microsoft.FSharp.Math.SI

any thoughts on this?

Thanks

~sparky

3. andrewkennedy says:

alexey_r:

Units actually don’t get seen at all by the CLR – they are "erased". They are a purely static phenomenon, which means that the performance of code with units is no worse than the performance of the code with the units removed. You can think of them as a "refinement" of usual .NET types, seen only by F#. Of course this has some downsides (no runtime inspection of units) but our experience so far has been that they are in any case extremely useful in practice. And interestingly, all the "classical" functional languages (Haskell, Caml, OCaml, Standard ML) have purely static type systems.

sparky: did you include

open Microsoft.FSharp.Math

? This has PhysicalConstants as a submodule.

• Andrew.
4. WillSmith says:

Would be nice to have unit of measure support as a general class library in the .net framework.  Can you specify the scale and precision as well?

5. aatreya says:

Andrew,

How can I get the following to compile?:

[<Measure>]

type km =

static member toM = 1.0/1000.0<m/km>

[<Measure>]

type m =

static member toKm = 1000.0<km/m>

Seems like there’s a fundamental limitation I’m running up against… or hopefully I’m just missing something.  The definition of m requires the definition of km, and vice versa.

6. andrewkennedy says:

You can define mutually recursive measures using "and" to connect them and placing the Measure attribute immediately before the name of the measure:

type [<Measure>] km =

static member toM = 1.0/1000.0<m/km>

and [<Measure>] m =

static member toKm = 1000.0<km/m>

7. aatreya says:

Wonderful – thanks!  I’m so excited that a practical programming language finally has units-of-measure capability.  Nice work.

8. sparky00 says:

This is incredibly annoying.

[1] Reference FSharp Powerpack.dll – check

[2] open Microsoft.FSharp.Math – Alt+Enter -> FSI pass

[3] open SI – Alt+Enter -> FSI FAIL [error FS0039: The namespace or module ‘SI’ is not defined.]

doing something as simple as:

#light

open Microsoft.FSharp.Math

open SI

let distance (d:float<m>) = d

does not work -> anyone else has this problem?

The VS IntelliSense and the documentation says that the SI module is installed, yet when I open and try to use it, nothing works.

It seems to me that the problem is with the FSI and interpreting namespaces/modules but if anyone can give me some insights, that will really help!

9. int19h says:

As far as F# is concerned, ft and m have nothing to do with each other. It’s up to you, the programmer, to define appropriate conversion factors.

This is mildly annoying. There’s no reason why feet cannot be converted to meters and back automatically (it’s not a narrowing conversion, so no opportunity for data loss), and even less for not being able to convert between m and km. It would seem to me that boost::units approach, which differentiates between abstract measures (duration, distance etc), and specific units of measurement, is more convenient.

10. davidacoder says:

This whole thing is seriously cool. Just today we stubmled again with unit mix up in our simulation code.

But then, my most urgent question would be whether there are any plans to support this in other languages as well? F# is nice, but we have a large investment in C# and really wouldn’t want to move to F# just for the unit stuff… Any ideas on that?

11. sparky00 says:

I finally got this to work and I realized where the problem was. I was using the default F# project and .fs file to type up all the code in but when I Alt+Enter and send to FSI, that is where the problem started.

Even though I added the reference via "Add Reference", I still get the error because FSI is not aware of project settings and all that. So in addition, I had to do #r "FSharp.PowerPack.dll" in the FSI window first, then Alt+Enter the rest of the code to it. Works now.

Thanks for the help.

~sparky

12. int19h says:

It would seem to be that Microsoft is deliberately pushing for F# for scientific computing now, while C# is your typical "general-purpose" (read: system & LOB) language. C# 4.0 is expected to have 4 new language features, one of them generic variance – I wouldn’t expect to see units among the other three. Nor anytime soon – DbC, for example, would probably be higher-priority for C#.

13. yemi says:

Hello,

One thing that I stumbled upon. The value conversion on the types get masked when you set a literal with the same name as a type. In FSI:

[<Measure>] type km = static member pM = 1000.0<km/m>;;

[<Measure>] type m = static member pK = 0.001<m/km>;;

let km = 1.0<km>;;

km;;

m.pK;;

km.pM;;

The last line errors saying it can’t find pM. Good day.

Yemi Bedu

14. Does F# support units with different zero values, e.g. Kelvin and Celsius? What I mean is that conversion of 100C to K depends on whether 100C in this case means temperature of boiling water (then the result is 373.15K) or difference between boiling water temperature and freezing water temperature (in this case the answer is 100K).

One can write two converters (say C.AbsToK and C.DeltaToK), but then it is user’s responsibility to do it right – F# will do no checking.

Or maybe one should define two types of K and C units – absolute and delta, with rules like AbsX-AbsX=DeltaX, AbsX+DeltaX=AbsX, AbsX+AbsX=illegal, and so on?

15. andrewkennedy says:

Good point regarding units with different zero values. The units supported by F# are those for which multiplication makes sense, and Celsius doesn’t fit this model. It makes no sense to double an absolute temperature measured in degrees Celsius. Whereas for Kelvin, this is fine.

A similar situation arises with absolute times versus time periods – it would indeed be nice to permit subtraction of absolute times, and rule out addition of absolute times, but permit either operation on time periods. One can do this by defining separate types, a little like DateTime and TimeSpan from the .NET framework.

16. First, let me remind you that in my new ongoing quest to read source code to be a better developer ,

17. Anders Cui says:

NASA气象卫星意外坠落说明，计量单位绝非小事。为编程语言添加对计量单位的支持可以很大程度上避免这样的错误，编程任务也变得更有趣。F#提供了对计量单位的静态检查，并且封装了国际单位制的各个单位和物理常量，另外我们也可以定义自己的单位；在单位之间进行换算也很简单；此外F#还支持计量单位的泛型。作为对NASA气象卫星的纪念，本文最后给出了一个模拟太阳系的例子 🙂

18. Kean over at AutoDesk (think AutoCAD etc.) is running an F# programming contest ! I’ve included his post

19. Ostatnio ponowiłem wysiłki do opanowania nowych języków z rodziny MS w tym także implementacji znanych

20. Ostatnio ponowiłem wysiłki do opanowania nowych języków z rodziny MS w tym także implementacji znanych

21. Michael says:

It's been a little while and there have been several developments in and around C# and F# since this time. Where might one find the PowerPack, particularly concerning units, measures, conversions, etc? Thank you…