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?
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)