The F# compiler includes a Units of Measure feature which infers a measure type at compile time, which means you get measure type safety with uncompromised runtime performance.

Example of F#’s built-in Units of Measure feature (hover over shows inferred type):

Sometimes you might also want to actually infer units of measure at runtime, say to display the inferred unit type at the UI. The following F# code prototype provides such inference.

Lets start by defining some metres and seconds unit types:

let m = UnitType.Create("m") let s = UnitType.Create("s")

Now with the source below we can explore types in F# Interactive:

> 10.0 * m;; val it : UnitValue = 10 m {Unit = Unit ("m",1); Value = 10.0;} > 10.0 * m / 5.0 * s;; val it : UnitValue = 2 m s {Unit = CompositeUnit [Unit("m",1); Unit("s",1)]; Value = 2.0;}

Source code to F# runtime units of measure:

type UnitType = | Unit of string * int | CompositeUnit of UnitType list static member Create(s) = Unit(s,1) override this.ToString() = let exponent = function | Unit(_,n) -> n | CompositeUnit(_) -> raise (new System.InvalidOperationException()) let rec toString = function | Unit(s,n) when n=0 -> "" | Unit(s,n) when n=1 -> s | Unit(s,n) -> s + " ^ " + n.ToString() | CompositeUnit(us) -> let ps, ns = us |> List.partition (fun u -> exponent u >= 0) let join xs = let s = xs |> List.map toString |> List.toArray System.String.Join(" ",s) match ps,ns with | ps, [] -> join ps | ps, ns -> let ns = ns |> List.map UnitType.Reciprocal join ps + " / " + join ns match this with | Unit(_,n) when n < 0 -> " / " + toString this | _ -> toString this static member ( * ) (v:ValueType,u:UnitType) = UnitValue(v,u) static member ( * ) (lhs:UnitType,rhs:UnitType) = let text = function | Unit(s,n) -> s | CompositeUnit(us) -> us.ToString() let normalize us u = let t = text u match us |> List.tryFind (fun x -> text x = t), u with | Some(Unit(s,n) as v), Unit(_,n') -> us |> List.map (fun x -> if x = v then Unit(s,n+n') else x) | Some(_), _ -> raise (new System.NotImplementedException()) | None, _ -> us@[u] let normalize' us us' = us' |> List.fold (fun (acc) x -> normalize acc x) us match lhs,rhs with | Unit(u1,p1), Unit(u2,p2) when u1 = u2 -> Unit(u1,p1+p2) | Unit(u1,p1), Unit(u2,p2) -> CompositeUnit([lhs;rhs]) | CompositeUnit(us), Unit(_,_) -> CompositeUnit(normalize us rhs) | Unit(_,_), CompositeUnit(us) -> CompositeUnit(normalize' [lhs] us) | CompositeUnit(us), CompositeUnit(us') -> CompositeUnit(normalize' us us') | _,_ -> raise (new System.NotImplementedException()) static member Reciprocal x = let rec reciprocal = function | Unit(s,n) -> Unit(s,-n) | CompositeUnit(us) -> CompositeUnit(us |> List.map reciprocal) reciprocal x static member ( / ) (lhs:UnitType,rhs:UnitType) = lhs * (UnitType.Reciprocal rhs) static member ( + ) (lhs:UnitType,rhs:UnitType) = if lhs = rhs then lhs else raise (new System.InvalidOperationException()) and ValueType = float and UnitValue(v:ValueType,u:UnitType) = member this.Value = v member this.Unit = u override this.ToString() = sprintf "%O %O" v u static member (+) (lhs:UnitValue,rhs:UnitValue) = UnitValue(lhs.Value+rhs.Value, lhs.Unit+rhs.Unit) static member (*) (lhs:UnitValue,rhs:UnitValue) = UnitValue(lhs.Value*rhs.Value,lhs.Unit*rhs.Unit) static member (*) (lhs:UnitValue,rhs:ValueType) = UnitValue(lhs.Value*rhs,lhs.Unit) static member (*) (v:UnitValue,u:UnitType) = UnitValue(v.Value,v.Unit*u) static member (/) (lhs:UnitValue,rhs:UnitValue) = UnitValue(lhs.Value/rhs.Value,lhs.Unit/rhs.Unit) static member (/) (lhs:UnitValue,rhs:ValueType) = UnitValue(lhs.Value/rhs,lhs.Unit) static member (/) (v:UnitValue,u:UnitType) = UnitValue(v.Value,v.Unit/u)

**Implementation details:**

Unit (*UnitType*) computations are separate from value (*UnitValue*) computations. A single unit types (say metres) just has a name and a power (default of 1):

*let metres = Unit(name = “metres”, power = 1)*

To multiply by the same unit type, simply add the powers:

*(2.0 * metres) * (3.0 metres) = 6.0 metres ^ 2*

To handle composite unit types, when multiplying 2 unit values, first try to find a matching unit type in the existing list, if successful add the powers, otherwise add the new type:

*2.0 * metres * seconds = 2 .0 metres (per) second*

To divide simply multiply by the reciprocal:

*(2.0 * metres * seconds) / (1.0 * seconds) = 2.0 metres per second * 1.0 seconds ^ -1*

**Resources:**

## topsy.com

5/29/2010 4:01:33 PM |Pingback from topsy.com

Twitter Trackbacks for

Runtime Units of Measure for F#

[trelford.com]

on Topsy.com