Return F# 中相同类型的修改版本
Return a modified version of same type in F#
如果我有一个 class 层次结构,例如
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
并在每个 class 上实现它。这看起来像是很多额外的工作 plus 它很容易出错,因为 return 类型是 Employee
并且必须向上转换回原来的 class.
似乎应该有一种更简单、更安全的方法来完成这样的任务。这是需要 typeclasses 的东西吗?
更新
是的,我可以让 name
字段可变,这就是它现在在我的代码中的实现方式,但这正是我想要摆脱的。
更新 2
我提出的满足类型安全和简洁性要求的解决方案是定义
type Employee<'a> = {name: string; otherStuff: 'a}
然后只需使用 with
语法更改名称。但是 otherStuff: 'a
显然是丑陋且看起来很笨拙的代码,所以我仍然愿意接受更好的解决方案。
如果您正在寻找既纯粹又惯用的 F# 的东西,那么您首先不应该使用继承层次结构。那是一个面向对象的概念。
在 F# 中,您可以使用代数数据类型像这样对 Employee 建模:
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;}
如果我有一个 class 层次结构,例如
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
并在每个 class 上实现它。这看起来像是很多额外的工作 plus 它很容易出错,因为 return 类型是 Employee
并且必须向上转换回原来的 class.
似乎应该有一种更简单、更安全的方法来完成这样的任务。这是需要 typeclasses 的东西吗?
更新
是的,我可以让 name
字段可变,这就是它现在在我的代码中的实现方式,但这正是我想要摆脱的。
更新 2
我提出的满足类型安全和简洁性要求的解决方案是定义
type Employee<'a> = {name: string; otherStuff: 'a}
然后只需使用 with
语法更改名称。但是 otherStuff: 'a
显然是丑陋且看起来很笨拙的代码,所以我仍然愿意接受更好的解决方案。
如果您正在寻找既纯粹又惯用的 F# 的东西,那么您首先不应该使用继承层次结构。那是一个面向对象的概念。
在 F# 中,您可以使用代数数据类型像这样对 Employee 建模:
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;}