Search code examples
f#measure

F# measure unit : from radians to velocity in m/s


let's say I've defined an F# module for handling 3-dimensional vectors and measure unit:

[<Measure>]
type m
[<Measure>]
type s
[<Measure>]
type v = m/s
[<Measure>]
type rad

type Vector3<[<Measure>] 'a> =
    {
    X : float<'a>
    Y : float<'a>
    Z : float<'a>
    }

And somewhere I have an angle expressed in radians:

let angle:float32<rad> = 0.5f<rad>

Now I have to declare a Vector3 (velocity) using the angle to calculate its components. I tried something like :

let velocity : Vector3<m/s> = { X = Math.Cos(angle);Y = Math.Sin(angle);Z = 0.0<m/s>} //don't compile

The code above doesn't compile because Vector3 is expecting a value in X and Y but Sin returns a float.

How can I solve this problem? If it possible I would like to perform a conversion between measure units, instead of a cast, in such a way that the compiler can guarantee that I'm doing the right thing while transforming an angle into a velocity.

Any suggestion?


Solution

  • Several problems here: cos is expecting a unitless value, so you have to take the units off angle; given that velocity is expecting float and not float32, you might as well just convert directly to float (which removes the units).

    Then, you need to put the units back on. In first versions of units of measure, you could do this by simply multiplying by 1 in the appropriate measure. Now there is LanguagePrimitives.FloatWithMeasure, which is more correct, but slightly more verbose.

    let velocity =
     {
         X = angle |> float |> cos |> LanguagePrimitives.FloatWithMeasure<m/s> ;
         Y = angle |> float |> sin |> LanguagePrimitives.FloatWithMeasure<m/s> ;
         Z = 0.0<m/s> ;
     }
    

    That aside, 10 radians is a funny angle...

    (Note that cos and sin are built-in)