Units of Measure in C# - Almost

How do F# units of measure work?

According to a response on the next related blog post, they are a purely static mechanism in the F# compiler. So there is no CLR representation of the units data.

Its not entirely clear whether it currently works with non-float types, but from the perspective of the type system it is theoretically possible.

f# function call with parameters and cast to units of measure

Your 3. function definition is not a valid F# function definition. If you need to call another function 'FreshConcreteLoad()' you cannot use type annotations in function call.
Maybe you wanted to cast function parameters to units of measure but this cast cannot be performed because F# units of measure are not exist in C# so C# value cannot be directly casted to it.

What you could do is to create a couple of helper converting F# functions like:

let convToLength (inp: float) = inp * 1.0<m>
let convToDensity (inp: float) = inp * 1.0<kN/m^3>

and define 3rd function as:

let FreshConcreteLoadLUseMeasure(slabThickness:Length, freshConcreteDensity:Length) =  FreshConcreteLoad (convToLength slabThickness.[Length.Units.m], convToDensity freshConcreteDensity.[Length.Units.m]) 

or perform conversion directly in the call:

let FreshConcreteLoadLUseMeasure(slabThickness:Length, freshConcreteDensity:Length) =  FreshConcreteLoad (1.0<m> * slabThickness.[Length.Units.m], 1.0<kN/m^3> * freshConcreteDensity.[Length.Units.m])

Using real world units instead of types

One way to achieve this would be to use composition of the basic object (Temperature in your case) with a TemperatureTraits class that specializes the basic object. By analogy to C++, the String equivalent class basic_string is actually a class template (generic in C# terms) that has template parameters not only for the string element (char, wide char) but also a traits class that elaborates on how the class behaves for a given type of string element (e.g. char_traits).

In your case, you might define a generic like

public class MeasurableWithUnits<class M MEASURABLE, class U UNITS>

and then implementation would depend not only on the measurable class but also on the units class. How useful this would be in practice would depend on how much of such an object could be made truly generic - what operations are common across combinations of Measurable and Units?

There is a research paper on C# traits here, if this approach looks interesting.

How do you print the resulting units using units of measure in F#?

Unfortunately, this is not possible.

Units of measure exist only at compile time. When you compile the program, they will be ereased (because .NET doesn't have any way of representing units for types). This means that at the runtime, the result of your calculation will be just float. I don't think there is any way other than just writing units as string in your code...

There was a related question some time ago. It has some more details and also explains why you cannot get information about units using reflection.

  • Why can not use reflection in f#

How are units of measure represented?

F# stores 'extra type information' in a resource in the compiled assembly, and the F# compiler knows how to read that resource. So whereas a discriminated union is just compiled into, say, a class, and a unit of measure is erased into a double, there's extra F#-specific type info in a resource in the assembly so that when the F# compiler reads it, it can re-construct the extra "F# metadata".

The PowerPack has a metadata reader that lets you access it programmatically.

F#: Can units of measure be bound dynamically at runtime?

This isn't possible, since F# units-of-measure are erased (they only exist at compile-time).

You could author a library with a runtime implementation (I haven't thought about what a design would look like). But you probably lose the static checking.

I think possibly a better strategy may be to isolate the boundary, and at the boundary point (where you read from the database and infer the unit types) somehow get the right types into the type system, but depending on how the code is structured and what exactly you're doing, that may or may not be possible/easy...

Maintaining Units of measure across type converstions

(Caveat: I've not used units much in anger.)

I think that the only negative for using e.g. FloatWithMeasure is the unit-casting aspect (unitless to unitful). I think this is conceptually orthogonal to the numeric-representation-casting aspect (e.g. int to float). However there is (I think) no library function to do numeric-representation-casting on unit-ful values. Perhaps this is reflective of the fact that most unitful values model real-world continuous values, as so discrete representations like int are typically not used for them (e.g. 1<s> feels wrong; surely you mean 1.0<s>).

So I think it's fine to 'cast representations' and then 'readjust units', but I wonder how you got the values with different representations in the first place, as it's often typical for those representations to be fixed for a domain (e.g. use float everywhere).

(In any case, I do like your floatMeasure function, which un-confounds the unit-aspect from the representation-aspect, so that if you do need to only change representation, you have a way to express it directly.)



Related Topics



Leave a reply



Submit