首页 > 解决方案 > F# 中带有语句的跨类型记录?

问题描述

以下代码无法编译:

type Person = 
  {
    FirstName : string
    LastName : string
  }

type Employee = 
  {
    FirstName : string
    LastName : string
    Salary : int
  }

let p : Person = 
  {
    FirstName = "Steve"
    LastName = "Jobs"
  }

let e : Employee = 
  {
    p with Salary = 1
  }

一种解决方法是编写一个助手,可能像这样:

let toEmployee (p : Person) : Employee = 
  {
    FirstName = p.FirstName
    LastName = p.LastName
    Salary = Unchecked.defaultof<_>
  }

let e : Employee = 
  {
    toEmployee p with Salary = 1
  }

但这有点乏味。

F# 是否提供了更好的方法?

这在 JavaScript 中很常见,例如:

const e = {
  ...p,
  salary: 1,
};

标签: typesf#record

解决方案


作为一种静态和名义类型的语言,这在 F# 中是不可能的,这是有意设计的。

假设您确实具有 JS 和 value 之类的功能let e = {...p; Salary = 1}。然后假设您重命名Person. e应该推断出的类型是什么?错误信息应该说什么?这个问题没有简单的答案,而且在更大的代码库中可能会变得非常复杂且难以快速理解。

最好手动写出新记录。它有点冗长,但它符合 F# 的一般主题,即在某些维度上更加明确,因此您可以不那么具体地使用注释类型(允许更多类型推断)。如果您可以对两者都非常含蓄,那么它将造成复杂性的爆炸式增长。


在这里可能有帮助的另一个选项是更改您的模型以将重复的字段放入单独的类型中:

type Person = 
  { FirstName : string
    LastName : string }

type Employee = 
  { Person : Person
    Salary : int }

let p = 
  { FirstName = "Steve"
    LastName = "Jobs" }

let e = { Person = p; Salary = 1 }

在 F# 中,为类型建模的方式非常重要,并且会对所有使用它的代码产生连锁反应。因此,如果您的代码看起来很不稳定,请考虑是否可以改进您的类型。


推荐阅读