Asik
Asik

Reputation: 22133

How to declare a generic conversion operator that preserves units of measure?

The built-in conversion operators in F# eliminate units of measure. I'd like to define ones that preserve them. I can do it fine for one specific conversion, e.g. int to float:

let inline intToFloat (x:int<'u>) =
    x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

But I don't know what would be the syntax for a generic ((anything with an op_Implicit ^m -> float) -> float) operator:

let inline floatM (x:^m<'u>) =
    x |> float |> LanguagePrimitives.FloatWithMeasure<'u>
// FS0712: Type parameter cannot be used as type constructor

Is it possible at all to do this?

Upvotes: 3

Views: 153

Answers (2)

Asik
Asik

Reputation: 22133

A workaround is to use overloading rather than generics:

type Conv =
    static member inline toDouble<[<Measure>] 'u> (x: float32<'u>) = 
        x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

    static member inline toDouble<[<Measure>] 'u> (x: sbyte<'u>) = 
        x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

    static member inline toDouble<[<Measure>] 'u> (x: int16<'u>) = 
        x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

    static member inline toDouble<[<Measure>] 'u> (x: int<'u>) = 
        x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

    static member inline toDouble<[<Measure>] 'u> (x: int64<'u>) = 
        x |> float |> LanguagePrimitives.FloatWithMeasure<'u>

And now:

let f = Conv.toDouble (45<second>) // f is a float<second>

I'd love to be able to declare an operator to hide away the static class, something like:

let inline floatM< ^m, [<Measure>]'u when (^m) : (static member toFloat: ^m<'u> -> float<'u>)> (x:^m) =
    Conv.toDouble x

But that still runs into the same limitation that a generic type cannot have a generic unit of measure.

I'll accept my own answer until someone comes up with a better solution.

Upvotes: 1

czifro
czifro

Reputation: 784

I did it as such:

let inline toIntWithMeasure<[<Measure>] 'a> (x:obj) =
  match x with
  | :? int as i -> i |> LanguagePrimitives.Int32WithMeasure<'a>
  | _ -> failwith "Not an int!"

Upvotes: 1

Related Questions