Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Units of Measurement modeling metric prefix (micro, milli, nano)

As per this question: Fractional power of units of measures in F# there are no fractional powers supported for units of measure in F#.

In my application, it is beneficial to think of data with a metric prefix sometime, e.g. when dealing with seconds. Sometimes I need a result in milli-seconds, sometimes in seconds.

The alternative I'm currently thinking about using is this

[<Measure>] type milli
[<Measure>] type second

let a = 10.0<second>;
let b = 10.0<milli*second>

which gives me:

val a : float<second> = 10.0
val b : float<milli second> = 10.0

Now I want to allow calculations with the two operations. So I could do

let milliSecondsPerSecond = 1000.0<(milli*second)/second>

let a = 10.0<second>;
let b = 10.0<milli*second>

(a*milliSecondsPerSecond) + b

which gives me exactly what I wanted

val it : float<milli second> = 10010.0

Now, this is all nice and shiny but grows out of hand quickly when you want to support multiple units and multiple prefixes. So I think it would be either necessary to bake this into a more generic solution, but don't know where to start. I tried

let milliPer<'a> = 1000.0<(milli * 'a) / 'a>

but that won't work because f# complains and tells me "Non-Zero constants cannot have generic units"...

Since I imagine that unit prefixes are a common problem, I imagine someone has solved this problem before. Is there a more idiomatic way to do unit prefixes in F#?

like image 525
Johannes Rudolph Avatar asked Oct 26 '12 17:10

Johannes Rudolph


1 Answers

You write the constant as 1000.0<(milli second)/second> representing 1000 milliseconds per second, but actually (you can do this as an algebraic simplification) "milli" just means that you need to multiply whatever unit by 1000 to get the unit without the "milli" prefix.

So, you can simplify your definition of milliPer (and milliSecondsPerSecond) to just say:

let milli = 1000.0<milli>

Then it is possible to use it with other kinds of measures:

(10.0<second> * milli) + 10.0<milli second>   
(10.0<meter> * milli) + 10.0<milli meter>

I think this should not lead to any complications anywhere in the code - it is a perfectly fine pattern when working with units (I've seen people using a unit of percentsimilarly, but then the conversion is 0.01)

like image 132
Tomas Petricek Avatar answered Nov 15 '22 06:11

Tomas Petricek