F#运算符重载,用于转换多个不同的度量单位

时间:2014-02-18 18:26:48

标签: f# units-of-measurement f#-3.0

我希望能够做到这一点:

let duration = 1<hours> + 2<minutes> + 3<seconds>

具有以下类型和功能(可能还有更多计量单位):

type [<Measure>] seconds     
type [<Measure>] minutes
type [<Measure>] hours

let seconds_per_minute = 60<seconds> / 1<minutes>
let minutes_per_hour = 60<minutes> / 1<hours>

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes

因此,基本上应该使用“hours_to_minutes”来添加小时和分钟,并且当我在上面输入时,应该使用“minutes_to_seconds”来添加分钟和秒。

这可以用F#吗?

2 个答案:

答案 0 :(得分:15)

实际上有可能,有办法做到这一点:

type [<Measure>] seconds     
type [<Measure>] minutes
type [<Measure>] hours

let seconds_per_minute = 60<seconds> / 1<minutes>
let minutes_per_hour = 60<minutes> / 1<hours>

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes

type D1 = D1
type D2 = D2

type Sum = Sum with
  static member inline ($) (Sum, _:^t when ^t: null and ^t: struct) = id
  static member inline ($) (Sum, b)              = fun _  _  a -> a + b
  static member        ($) (Sum, b:int<minutes>) = fun D1 _  a -> hours_to_minutes   a b
  static member        ($) (Sum, b:int<seconds>) = fun D1 D2 a -> minutes_to_seconds a b    

let inline (+) a b :'t = (Sum $ b) D1 D2 a

let duration = 1<hours> + 2<minutes> + 3<seconds>

但它真的很hacky,我不推荐它。

<强>更新

根据评论,这里有一些答案:

  • 此技术使用在编译时解析的重载,因此在运行时不会降低性能。它基于我前一段时间在my blog写的内容。

  • 要添加更多重载,您必须添加更多虚拟参数(D3D4,...),最终如果您决定添加一些与现有参数冲突的重载您可能必须使用三元运算符(?<-)或具有显式静态成员约束的函数调用。 Here's a sample code

  • 我想我不会使用它,因为它需要很多hacks(Dummy重载和2个虚拟类型)并且代码变得不那么可读。最终,如果F#基于重载增加了对内联函数的更多支持,我肯定会考虑它。

  • Phil Trelford's technique(在Reed的回答中提到)在运行时工作,第三种选择是使用幻像类型,它可能需要更少的黑客攻击。

<强>结论

如果我必须在所有选择之间做出选择,我会使用这种技术,但在呼叫网站上更明确,我的意思是我会定义转换函数,如minutesseconds,并在调用时使用网站我会写:

let duration = seconds 1<hours> + seconds 2<minutes> + 3<seconds>

然后定义那些我会使用重载的转换函数,但它不如重新定义现有的二元运算符那么hacky。

答案 1 :(得分:5)

这在F#中是不可能的。如果不指定类型的转换,则无法直接“自动转换”成功。您必须明确调用转换函数(seconds_per_minute等)。

然而,Phil Trelford展示了一种机制,你可以create runtime classes which do support this使用,但语法略有不同。使用他的类型,你可以写:

let duration = 1.0 * SI.hours + 2.0 * SI.minutes + 3.0 * SI.seconds