问题
When writing generic functions in F#, I can use members defined in LanguagePrimitives module, like e.g. in this function, that simply increments a number
let inline increment (x : 'a) =
x + LanguagePrimitives.GenericOne
I wonder if there's anything similar to work with units of measure. In particular: is it possible to write a function that takes a generic argument and converts it to a number of the same type with a unit. Something like:
let inline toNumberWithUnit<[<Measure>] 'u> x =
x * GenericOne<'u> //that won't work
This would have a type: 'a -> 'a<'u>. Is it possible ?
回答1:
That signature would be illegal in .NET so the only way will be to use F# inline feature with static constraints.
Then you can define a function like this:
[<Measure>]
type M = class end
let x = LanguagePrimitives.FloatWithMeasure<M> 2.
type T<[<Measure>]'M>() =
static member ($) (T, x) = LanguagePrimitives.FloatWithMeasure<'M> x
static member ($) (T, x) = LanguagePrimitives.Float32WithMeasure<'M> x
static member ($) (T, x) = LanguagePrimitives.Int32WithMeasure<'M> x
// more overloads
let inline NumberWithMeasure x = T() $ x
let a: float<M> = NumberWithMeasure 2.
let b: float32<M> = NumberWithMeasure 2.0f
let c: int<M> = NumberWithMeasure 2
The main problem when dealing with generic numbers and units of measure is that you end up with those signatures where you a have a generic type with a type parameter (a higher kind ) which at the moment are not supported in .NET.
UPDATE
After a while I ran into this situation as well and found this answer that happens to come from me :)
After trying it with different units of measures I realized it doesn't work, because the type inference doesn't generalize over units of measure, the posted example works because type inference witness the use with the M
measure and then specializes the function over M
.
However here's a way to make it work, by explicitly using the $
operator above defined:
[<Measure>] type km
[<Measure>] type miles
type WithMeasure<[<Measure>]'M>() =
static member ($) (x, T) = LanguagePrimitives.FloatWithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.Float32WithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.Int32WithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.DecimalWithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.Int16WithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.Int64WithMeasure<'M> x
static member ($) (x, T) = LanguagePrimitives.SByteWithMeasure<'M> x
// no more overloads
let a: float<km> = 2. $WithMeasure()
let b: float32<miles> = 2.0f $WithMeasure()
There might be a way to create a generic function, or a generic constant, but at the moment it seems not to be possible with the current version of F#.
I will try with F# 4.1 when it's ready.
来源:https://stackoverflow.com/questions/28786971/generic-units-in-f