5

I am calling external function requiring float[], but my array is float<m>[]. How could I strip the unit of measure from array?

I need something like the function below, but this does not compile. And I would like to avoid any iterating or duplicating of the array, as float<m> and float values are identical...

let demeasure (arr:float<m>[]): float[] = float[] (arr)
Gus
  • 25,839
  • 2
  • 51
  • 76
mca
  • 53
  • 2

4 Answers4

4

I believe that a cast to obj, followed by a dynamic cast to float[] would work, e.g.

(arr :> obj) :?> float[]

because there is no runtime representation.

Possibly see also

F# Units of measure - 'lifting' values to float<something>

How to generically remove F# Units of measure

Community
  • 1
  • 1
Brian
  • 117,631
  • 17
  • 236
  • 300
2

Here's a generic solution for any numeric type, any unit of measure but for Arrays only. If we had Higher Kinds it could be made generic over the container as well:

let inline retype (x: 'T) : 'U = (# "" x: 'U #)

module Array =
    let inline stripUoM (x: '``Num<'M>`` []) =
        let _ = Unchecked.defaultof<'``Num<'M>``> * (LanguagePrimitives.GenericOne : 'Num)
        retype x :'Num []
// Usage

[<Measure>]type m

let x = [|34.0<m>; 9.0<m>; 42.0<m> |] |> Array.stripUoM
// val x : float [] = [|34.0; 9.0; 42.0|]

let y = [|34<m>; 9<m>; 42<m> |] |> Array.stripUoM
// val y : int [] = [|34; 9; 42|]
Gus
  • 25,839
  • 2
  • 51
  • 76
  • Hmm how does `Unchecked.defaultof<'``Num<'M>``> * (LanguagePrimitives.GenericOne : 'Num)` prevent "FS0030: Value restriction" error? Also how did you come up with `x: '``Num<'M>`` ` ? Surprised to see the compiler accepts it – nodakai Mar 02 '23 at 12:10
  • I don't see why there should be a value restriction, the goal of that line is to make the compiler infer a type called `'Num` without caring about the result. Regarding the other type variable, the F# compiler accepts whatever is written between double quotes, but it interprets it as a string the represents a single identifier without giving it a special meaning, so the `<'M>` inside it is not a type variable application, it could be anything without any specific meaning for the compiler. – Gus Mar 04 '23 at 07:10
  • Thanks. I see, so renaming `'``Num<'M>`` ` to `'T` doesn't change the bahavior of your function. And SRTP tells the compiler to find a `'Num` such that `static member (*): 'T -> 'Num -> 'Any` is valid... something like that? That rocks! There seems to be a bit of gap between what I learned as HM type inference/unification and what's powering an industrial-strength compiler... – nodakai Mar 05 '23 at 06:51
  • Yes, type equations are powered simply by overload, the compiler uses the constraint solver in order to do overload resolution, once an overload is select, type inference continues with the types unified with the selected overload. – Gus Mar 05 '23 at 10:08
1
[<Measure>]type m
let f (arr : float[]) : float = 0.0
let arr = [|1.0<m>|]
f (unbox (box arr))
desco
  • 16,642
  • 1
  • 45
  • 56
1
let demeasure (arr: float<m>[])  = arr |> Array.map (fun i -> float i)
Abel
  • 56,041
  • 24
  • 146
  • 247
Joshua
  • 4,099
  • 25
  • 37