Units of Measure in F#: Part Three, Generic Units


In the first two articles in this series, we saw how to declare base and derived units, introduce constants with units, define our own conversion functions, and have F# check the units for us.


But what if we’re writing code that doesn’t care what units it is working in? That’s the subject of today’s article.


Let’s start simple. What is the type of fun x -> x*x? Well, multiplication is overloaded, so F# defaults to integers:


image


But if we annotate the argument, we can define squaring for all kinds of floats (hovering over the last of these to show its type):


image


This looks painful! What if we don’t tell F# what the units are, using the underscore notation to say “find me its units”?


image


F# has inferred a generic unit type for squaring. The notation ‘u looks like a type parameter, but is in fact a unit-of-measure parameter that can be instantiated at any units. Let’s use it on masses, lengths and speeds:


image


F# can infer generic unit-of-measure types with type annotations required only to resolve overloading. Here are some simple examples, tested out in F# Interactive:


image


Here’s one that requires a bit of head-scratching to understand:


image 


Returning to reality, suppose we want to sum the elements of a list. In true functional style, we use one of the fold operators.


image


Oops – we don’t seem to have a nice generic type! The reason is simple: unless units are specified, constants, including zero, are assumed to have no units, i.e. to be dimensionless. So instead, let’s give 0.0 some units, but not tell F# what they are, by writing 0.0<_>:


image 


That’s better!


Now we can go wild and write all sorts of statistical stuff.


image


And here are the beautiful types that are inferred for these functions:


image


Summing up: we’ve seen how to write code that is generic (a.k.a. as polymorphic) in units-of-measure.


Next time, we’ll write types that are generic in units-of-measure. This makes the feature extensible: if floats with units aren’t what you want, just define your own types!

Comments (10)

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

  2. CarlMon says:

    Thanks for the great post!

    I am trying to get my head around UoM…  Is it possible to define partially generic units?  Consider my attempt at currency conversion:

    [<Measure>]

    type USD =

    static member convert (value : float<USD>) (ratio : float< _ / USD>) = value * ratio

    The static method has signiture:

     "value:float<USD> -> (float<‘a0> -> float<USD/’a0>)"

    it converts a ratio of type float<EUR / USD> correctly but does not limit the ratio USD exchange rates.  I would like this signiture to be:

     "value:float<USD> -> (float<‘a0 / USD> -> float<‘a0>)"

  3. xelatihy says:

    Andrew,

    I do wonder if the 4th part of the series will be coming soon. I am using this feature for a new research project and have found it impossible to be able to. My problem is defining 3D vectors/points that are generic with respect to the units of measures. I have had significant trouble in particular with operator overloading. Any help would be fantastic!

  4. F# es un lenguaje funcional, creado por Microsoft. Implementado bajo el soporte de .NET CLR, es un lenguaje

  5. Anders Cui says:

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

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

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

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

  9. Joseph says:

    n my case units of measure, especially for weight-quantities and currencies and combinations of them, are in a database.

    There could be MT, KG, LBS, but also custom units such as Bags (for coffee), Barrels (for oil), and all other kinds of unusual units. We store conversion factors between them as well.

    Can we use the F# units with such dynamically configurable units?

    thanks,

    Joseph