F#中的通用单位 [英] Generic units in F#

查看:114
本文介绍了F#中的通用单位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在F#中编写泛型函数时,我可以使用在LanguagePrimitives模块中定义的成员,例如在这个函数中,只需增加一个数字

  let inline increment(x:'a)= 
x + LanguagePrimitives。 GenericOne

我想知道是否有与测量单位相似的任何东西。特别是:是否可以编写一个采用泛型参数的函数,并将其转换为与单位相同类型的数字。例如:

  let inline toNumberWithUnit< [< Measure>]'u> x = 
x * GenericOne< u> //这不起作用

这将会有一个类型:'a - >'a< U>。是否有可能?

解决方案

这个签名在.NET中是非法的,所以唯一的方法就是将F#内联特性与static约束。



然后你可以定义一个这样的函数:

  [< Measure>] 
type M = class end

let x = LanguagePrimitives.FloatWithMeasure< M> 2.

type T< [< Measure>]'M>()=
static member($)(T,x)= LanguagePrimitives.FloatWithMeasure< M> x
静态成员($)(T,x)= LanguagePrimitives.Float32WithMeasure< M> x
静态成员($)(T,x)= LanguagePrimitives.Int32WithMeasure< M> x
//更多重载

让内联NumberWithMeasure x = T()$ x

let a:float< M> = NumberWithMeasure 2.
let b:float32< M> = NumberWithMeasure 2.0f
让c:int< M> = NumberWithMeasure 2

处理通用数字和度量单位时的主要问题是,那些你有一个具有类型参数的泛型类型的签名(更高类型)目前在.NET中不支持。

更新



后有一段时间我遇到了这种情况,并且发现这个答案恰好来自我:)

在用不同的度量单位尝试它之后,我意识到它没有工作,因为类型推断没有通过度量单位进行概括,所以发布的示例工作是因为类型推断见证了 M 度量的用法,然后将函数专用于 M



然而,通过显式使用 $ code>上面定义的操作符:

  [ < Measure>]类型km 
[< Measure>]类型miles

Type WithMeasure< [< Measure>]'M> =
static member($ )(x,T)= LanguagePrimitives.FloatWithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.Float32WithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.Int32WithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.DecimalWithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.Int16WithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.Int64WithMeasure< M> x
静态成员($)(x,T)= LanguagePrimitives.SByteWithMeasure< M> x
//不再有超载


让a:float< km> = 2. $ WithMeasure()
let b:float32< miles> = 2.0f $ WithMeasure()

可能有办法创建一个通用函数或通用不变,但目前似乎不可能使用当前版本的F#。



在准备就绪时,我会尝试使用F#4.1。


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 ?

解决方案

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.

这篇关于F#中的通用单位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆