Generic units in F#

回眸只為那壹抹淺笑 提交于 2019-12-01 11:53:59

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!