2014-02-18 45 views
12

的多個不同單位的轉換我希望能夠做到這一點:F#的運算符重載的措施

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#中做的嗎?

回答

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> 

但它確實哈克,我不會推薦它。

UPDATE

基於這裏的評論是一些答案:

  • 該技術使用在編譯時解析重載,所以在運行時沒有性能損失。這是基於我前段時間在my blog中寫的。

  • 要添加更多的重載,你將不得不增加更多的虛擬參數(D3D4,...),並最終如果你決定要添加一些重載與現有的衝突,那麼你可能需要使用一個三元操作(?<-)或具有顯式靜態成員約束的函數調用。 Here's a sample code

  • 我想我不會使用它,因爲它需要很多黑客(虛擬過載和2個虛擬類型),代碼變得不太可讀。最終,如果F#增加了對基於重載的內聯函數的更多支持,我肯定會考慮它。

  • Phil Trelford's technique(在裏德的回答中提到)在運行時工作,第三種選擇是使用幻像類型,它可能需要更少的黑客。

結論

如果我把所有的替代品之間選擇,我會用這種方法,但是是在調用點更加明確,我的意思是我會這樣定義minutesseconds和方式轉換功能在調用點我會寫:

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

然後定義這些轉換功能,我會用重載,但它是不是重新定義EXIS少哈克ting二元運算符。

+0

哇,這太棒了!我有幾個問題,但: 1.爲什麼你不推薦它? 2.我的類型安全性是否鬆動? 3.如果您不得不選擇,那麼您寧願使用Phil Trelfort的運行時解決方案嗎? 4.美元實際上做了什麼? 謝謝! – user3323923

+0

順便說一句,我希望我能接受兩個答案。 – user3323923

+0

@ user3323923,Gustavo使用括號'($)'定義了一個運算符'$'。 @Gustavo,我回答這個問題,你爲什麼不使用它?這似乎是一個合法的解決方案。 –

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