2010-04-19 163 views
3

此功能:模式匹配

let convert (v: float<_>) = 
    match v with 
    | :? float<m> -> v/0.1<m> 
    | :? float<m/s> -> v/0.2<m/s> 
    | _ -> failwith "unknown" 

產生錯誤

類型「浮動<‘U>’不具有任何適當的子類型,並且不能被用作類型測試或運行時強制的來源。

有沒有辦法如何模式匹配度量單位?

回答

6

由於@kvb詳細解釋,問題是度量單位是類型的一部分。這意味着float<m>是與float<m/s>不同的類型(並且不幸的是,該信息不作爲運行時值的一部分存儲)。

所以,你實際上試圖編寫一個函數,可以使用兩種不同類型的輸入。清潔功能的解決方案是定義一個可識別聯合,可容納無論是第一類還是第二類的值:

let convert v = 
    match v with 
    | M v -> v/0.1<m> 
    | MPS v -> v/0.2<m/s> 

您:

type SomeValue = 
    | M of float<m> 
    | MPS of float<m/s> 

然後你就可以使用普通的模式匹配寫函數我們需要明確地將這些值包裝到區分的聯合值中,但這可能是直接完成此操作的唯一方法(不需要對程序結構進行較大的更改)。對於正常類型,如intfloat,您也可以使用重載成員(在某些F#類型中聲明),但這對於度量單位不起作用,因爲在F#編譯器擦除後,簽名將相同單位信息。

3

你的方法有兩個問題。首先,當你在你的函數的定義,使用下劃線,這是一樣使用清爽型的變量,所以你的定義等同於以下內容:

let convert (v: float<'u>) = //' 
    match v with 
    | :? float<m> -> v/0.1<m> 
    | :? float<m/s> -> v/0.2<m/s> 
    | _ -> failwith "unknown" 

什麼錯誤消息告訴你的是編譯器知道v的類型爲float<'u>,並且float<'u>沒有適當的子類型,因此在進行類型測試以確定它是否爲float<m>或任何其他類型沒有意義。

你可能會嘗試解決這個問題,首先將v裝箱到一個對象中,然後進行一個類型測試。例如,如果你有一個list<'a>,並且想查看它是否爲list<int>,那麼這是行得通的,因爲有關通用對象的完整類型信息在運行時被跟蹤,包括泛型類型參數(值得注意的是,這與其他一些運行時工作)。不幸的是,F#測量單位在運行時會被擦除,所以在這裏不起作用 - 由於在運行時這個值只是一個普通的系統,所以系統無法推斷正確的測量類型 - F#的系統對於度量單位而言,在這方面與Java處理泛型類型的方式實際上非常相似。另一方面,你試圖做的事似乎相當可疑 - 在度量單位中通用的函數不應該根據度量類型做什麼不同的事情;他們應該是正確的參數。你究竟想達到什麼目的?它看起來不像一個與物理現實相對應的操作,這是F#測量類型的基礎。

+0

嗨, 感謝您的回答。我將不得不重新思考我的代碼。 一條評論: 爲什麼在使用Microsoft.FSharp.Math.SI模塊時,單位的值不會簡化爲<1/s>? – 2010-04-19 10:16:10

+1

@奧德里奇 - 我不清楚爲什麼這不是簡單的(我的閱讀規範表明它應該是),但F#知道這些措施是等價的(例如,你可以寫'let(x:float <1/s>)= 1.0 '和編譯器將允許它)。有可能像Pa這樣的縮寫並不總是爲了顯示目的而展開。 – kvb 2010-04-19 10:57:49

0

查看運行時的單位科編號http://msdn.microsoft.com/en-us/library/dd233243.aspx

我同意@kvb,我認爲最好的方法是傳遞一個對象。

我想這樣做,使用您的代碼結構:

let convert (v: float<_>) = 
    match v with 
    | :? float<m> -> v<m> 
    | :? float<inches> -> v * 2.54/100.0<m>