2013-08-07 75 views
3

是否有可能有一個函數調用不同記錄類型的相同標籤?例如,假設有兩條記錄,定義如下如何從多個記錄類型訪問相同的標籤?

type Pen = { 
    Diameter: float 
    InkColor: string 
} 

type Pencil = { 
    Diameter: float 
    Hardness: int 
    Blackness: int 
} 

我可以創建一個函數來訪問任何一種記錄類型的Diameter標籤嗎?現在,如果我定義了一支筆和一支鉛筆,編譯器會對使用哪種記錄類型感到困惑。問題是我不希望編譯器選擇某種類型,如果它選擇了某種類型,則允許使用這兩種類型。這個例子不會編譯,因爲它期望一支鉛筆。

let black_pen = { 
    Diameter = 0.7 
    InkColor = "Black" 
} 

let mechanical_pencil = { 
    Diameter = 0.5 
    Hardness = 1 
    Blackness = 2 
} 

let getDiameter writing_utility = 
    let {Diameter = dia} = writing_utility 
    dia 

printf "%A" (getDiameter black_pen) 

我現在看到我唯一的選擇是:

  1. 結合記錄與枚舉類型來告訴它是什麼物體。然後,模式匹配
  2. 使用類,而不是使用繼承
  3. 使用動態類型和反射來檢查標籤,並輸入

這將是很好,如果我可以使用泛型這樣的事情:

let getDiameter writing_utility = 
    let {Diameter<float> = dia} = writing_utility 
    dia 

這是隻要記錄有一個標籤「直徑」,並且是一個浮點數,它將返回值。

回答

5

你真的應該使用繼承這一點,但以下工作

let inline getDiameter (t:^q when ^q :(member Diameter:float)) = 
    (^q : (member Diameter:float) t);; 
+0

非常感謝你的金塊。 – Gorilla3D

2

情侶以下其他選項。

從提供映射 「的事情直徑」 到 「直徑」 作爲一個函數:

let getDiameter (util:'a) (diamFunc:'a->float) = 
    let dia = diamFunc util 
    dia 

getDiameter black_pen (fun x -> x.Diameter) 
getDiameter mechanical_pencil (fun x -> x.Diameter) 

或許更清潔,使用的DU(大量使用F#3.1命名DU場語法):

type WritingImplement = 
    | Pen of diameter:float * inkColor:string 
    | Pencil of float * int * int // 3.0 syntax 

let black_pen = Pen(diameter = 0.7, inkColor = "Black") 
let mechanical_pencil = Pencil(0.5, 1, 2) // 3.0 syntax 

let getDiameter = function 
    | Pen(diameter = d) -> d 
    | Pencil(d, _, _) -> d // 3.0 syntax 
2

想想getDiameter是做什麼的。它將something映射到float'a -> float,但它沒有任何意義,因爲'a意味着它可以是任何東西,並且將任何東西映射到float值而不知道它的值是行不通的。我們需要確保我們知道關於通過的東西的一些屬性以獲得值,即我們想要類似<something with Diameter> -> float的東西,並且表示對某物的約束的最佳方式是使用接口,使得簽名現在變成IWithDiameter -> float

相關問題