2014-02-15 97 views
1

我想到了以下問題:F# Unit of Measure, Casting without losing the measure type 請注意,我沒有試圖使用這個unbox代碼,我只是發現了一些奇怪的在回答問題時的行爲。F#'unbox float x'與'unbox int x'奇怪的編譯結果

爲什麼下面的代碼工作

let intToFloat (x:int<'u>) : float<'u> = unbox float x 
intToFloat 1<second> 

,而這會產生一個System.InvalidCastException:無法投類型的對象float32ToFloat @ 86-6爲鍵入「Microsoft.FSharp.Core.FSharpFunc` 2 [System.Single,System.Double]」。

let float32ToFloat (x:float32<'u>) : float<'u> = unbox float x 
float32ToFloat 1.0f<second> 

如果我把(float x)的代碼工作預計將在括號中,所以我想這一定是有些表達式求值/類型推理規則。這裏究竟發生了什麼,爲什麼在第二種情況下需要這些缺口?

+0

Tomas的答案解釋了錯誤,但作爲一個側面說明,'unbox'並不是最好的方法來做到這一點。您應該使用'LanguagePrimitives.FloatWithMeasure <'u>',這意味着在.NET字節碼中沒有操作,而'unbox'則添加了一些運行時類型檢查,在這種情況下這不是必需的。 – Tarmil

+0

@Tarmil,我意識到這一點。感謝您確認'FloatWithMeasure'是一個更好的選擇,但是(請參閱我對鏈接問題的回答)。你介意在那邊添加/確認一下嗎? –

+0

哦,我沒有在你關聯的問題中看到你的帖子。所以我們幾乎說了同樣的事情:) – Tarmil

回答

3

代碼片段中的微妙之處是unbox float x - 編譯器將其視爲(unbox float) x。其結果是,兩個函數實際上是對待像這樣:

let intToFloat (x:int<'u>) : float<'u> = 
    let f = unbox float in f x 

let float32ToFloat (x:float32<'u>) : float<'u> = 
    let f = unbox float in f x 

所以,你正在服用的float功能,鑄造它(不安全)爲另一種類型的函數,然後調用它。第一種情況爲int<'u> -> float<'u>,第二種情況爲float32<'u> -> float<'u>

我認爲第一個工程,因爲當編譯器看到float功能(沒有任何類型的註釋),默認爲int -> float,並且由於測量單位是在運行時被擦除,這可以轉換爲int<'u> -> float<'u>(但不第二種 - 因爲你正在進行不安全的演員)。

所以,主要問題是在你的實現中出現了錯誤的括號(這導致了一個非常微妙的問題)。我想你可能想要這樣的事情:

let intToFloat (x:int<'u>) : float<'u> = unbox (float x) 
let float32ToFloat (x:float32<'u>) : float<'u> = unbox (float32 x) 
+0

它會產生一些Interresting IL。 http://stackoverflow.com/a/21802111/17919 – gradbot