2015-06-21 49 views
3

返回相同類型的修改後的版本。如果我有一個類層次結構類似在F#

type Employee(name) = 
    member val name: string = name 

type HourlyEmployee(name, rate) = 
    inherit Employee(name) 
    member val rate: int = rate 

type SalariedEmployee(name, salary) = 
    inherit Employee(salary) 
    member val salary: int = salary 

而且我要的是更新name場在途純,這怎麼可能一個功能?一對夫婦未能選擇:

let changeName(employee: Employee) = 
    // no idea what class this was, so this can only return the base class 

let changeName<'a when 'a :> Employee>(employee: 'a) = 
    // 'a has no constructor 

我已經拿出正在虛擬Employee.changeName和實施上的每個類最接近的事。這看起來好像很多額外的工作由於返回類型爲Employee,因此很容易出錯,必須將其恢復爲原始類。

似乎應該有一個更簡單,更安全的方法來完成這樣的任務。這是什麼類型類是必要的?

更新

是的,我可以只作name場可變的,這是它是如何在我的代碼中實現了,但是這就是我想要得到遠離的東西。

更新2

一個解決方案,我想出來的,符合安全型和簡潔的要求,將是確定

type Employee<'a> = {name: string; otherStuff: 'a} 

,然後只用with語法更改名稱。但是otherStuff: 'a顯然是醜陋的,看起來很醜陋的代碼,所以我仍然開放更好的解決方案。

+1

如果你打算把你的財產可寫的,爲什麼不「簡單」讓[屬性可寫(https://msdn.microsoft.com/en -us/library/dd483467.aspx)? 'member val name = name with get,set' – Sehnsucht

+0

@Sehnsucht我應該提到,這就是我現在正在做的事情,但是想要更純粹的實現。 – lobsterism

回答

6

如果你正在尋找純粹的東西和慣用的F#,那麼你不應該在第一個地方使用繼承層次結構。這是一個面向對象的概念。

在F#中,你可以模範員工尚且如此,使用代數數據類型:

type HourlyData = { Name : string; Rate : int } 
type SalaryData = { Name : string; Salary : int } 

type Employee = 
| Hourly of HourlyData 
| Salaried of SalaryData 

這將使您能夠創建Employee值是這樣的:

> let he = Hourly { Name = "Bob"; Rate = 100 };; 

val he : Employee = Hourly {Name = "Bob"; 
          Rate = 100;} 

> let se = Salaried { Name = "Jane"; Salary = 10000 };; 

val se : Employee = Salaried {Name = "Jane"; 
           Salary = 10000;} 

您也可以定義一個函數以純粹的方式更改名稱:

let changeName newName = function 
    | Hourly h -> Hourly { h with Name = newName } 
    | Salaried s -> Salaried { s with Name = newName } 

這使得您更改現有Employee值這樣的名稱:

> let se' = se |> changeName "Mary";; 

val se' : Employee = Salaried {Name = "Mary"; 
           Salary = 10000;} 
+0

還有一點比我想要的更詳細;對於大量的情況,你必須重複'| Blah b - > Blah {b with Name = newName' for each case。我想我會使用這個想法,但是將其限制在「員工」記錄的子字段中。因此,'輸入Employee = {name:string,payData:PayData}',其中'PayData'是一個ADT。 – lobsterism