Fsharp 记录中的可选参数
Optional parameters in Fsharp records
我需要为我的 F Sharp 记录修改和添加新的 属性。但随后它会给出没有这个新字段的先前实例的错误。我把它设置为 Nullable ,但仍然出现同样的错误,请帮我解决这个问题
我想你的意思是 "optional" 和 "a field that I don't provide when instantiating the record" 一样。但是,不幸的是(或者幸运的是,取决于您的观点),F# 记录中没有可选字段这样的东西。您指定的所有记录字段 必须 在实例化时出现。
另见 this closely related question。
你可以考虑这个权衡:
使用 记录。每次更新记录时,F# 编译器都会尖叫并提醒您使用该记录的所有地方,您现在需要提供附加信息。如果您添加的字段是选项,则附加信息可以是 None
。最大的优势:您的代码本身不必处理所有可能的 "what to do if field1
is missing? What if field2
is missing, too?"
情况
使用一个class。当您更新 class 时,F# 编译器不会对您放入 class 中的信息强制执行任何类型的完整性检查。 (您可以将记录视为 classes,其中所有字段都是构造函数参数,并且必须提供所有字段)。因此,更新 class 定义不会产生任何开销,但您的代码需要处理所有缺失值。
我个人更喜欢记录,因为它迫使我思考添加新字段的含义。
当然有一个中间立场:您可以使用记录,但通过静态成员或类似的东西实例化所有记录:
type Name =
{
First: string
Family: string
}
static member Create(first, family) = { First = first; Family = family}
如果在您的代码中,您总是使用 Name.Create
来实例化记录,您当然可以添加一个 MiddleName
字段,而不会引起任何消费者代码的注意。
Option type 在 F# 中优于 null,原因很简单 "uninitialized variables" 在函数式思维方式中不存在。
让我们先建立一个代表 VacationRequest
的记录,它必须得到老板的批准。
type VacationRequest =
{Name : string
Date : DateTime
Approval : string option}
你的方法的问题是所有字段都必须在构造时分配,所以这不会编译:
let holiday =
{Name = "Funk"
Date = DateTime(2020,12,31)}
您可以使用辅助函数解决此问题,该函数隐式设置选项值。
let fillInRequest name date =
{Name = name
Date = date
Approval = None}
现在您可以使用辅助函数构建记录。
let holiday = fillInRequest "Funk" <| DateTime(2020,12,31)
将代码发送给 FSI 时注意到一些有趣的事情。
val holiday : VacationRequest = {Name = "Funk";
Date = 31/12/2020 12:00:00 ;
Approval = null;}
老板然后可以更新请求(创建 新 记录)
let approvedHoliday =
{holiday with Approval = Some "boss' name"}
val approvedHoliday : VacationRequest = {Name = "Funk";
Date = 31/12/2020 12:00:00 ;
Approval = Some "boss' name";}
或原样发回
let betterLuckNextTime = holiday
我需要为我的 F Sharp 记录修改和添加新的 属性。但随后它会给出没有这个新字段的先前实例的错误。我把它设置为 Nullable ,但仍然出现同样的错误,请帮我解决这个问题
我想你的意思是 "optional" 和 "a field that I don't provide when instantiating the record" 一样。但是,不幸的是(或者幸运的是,取决于您的观点),F# 记录中没有可选字段这样的东西。您指定的所有记录字段 必须 在实例化时出现。
另见 this closely related question。
你可以考虑这个权衡:
使用 记录。每次更新记录时,F# 编译器都会尖叫并提醒您使用该记录的所有地方,您现在需要提供附加信息。如果您添加的字段是选项,则附加信息可以是
None
。最大的优势:您的代码本身不必处理所有可能的 "what to do iffield1
is missing? What iffield2
is missing, too?" 情况
使用一个class。当您更新 class 时,F# 编译器不会对您放入 class 中的信息强制执行任何类型的完整性检查。 (您可以将记录视为 classes,其中所有字段都是构造函数参数,并且必须提供所有字段)。因此,更新 class 定义不会产生任何开销,但您的代码需要处理所有缺失值。
我个人更喜欢记录,因为它迫使我思考添加新字段的含义。
当然有一个中间立场:您可以使用记录,但通过静态成员或类似的东西实例化所有记录:
type Name =
{
First: string
Family: string
}
static member Create(first, family) = { First = first; Family = family}
如果在您的代码中,您总是使用 Name.Create
来实例化记录,您当然可以添加一个 MiddleName
字段,而不会引起任何消费者代码的注意。
Option type 在 F# 中优于 null,原因很简单 "uninitialized variables" 在函数式思维方式中不存在。
让我们先建立一个代表 VacationRequest
的记录,它必须得到老板的批准。
type VacationRequest =
{Name : string
Date : DateTime
Approval : string option}
你的方法的问题是所有字段都必须在构造时分配,所以这不会编译:
let holiday =
{Name = "Funk"
Date = DateTime(2020,12,31)}
您可以使用辅助函数解决此问题,该函数隐式设置选项值。
let fillInRequest name date =
{Name = name
Date = date
Approval = None}
现在您可以使用辅助函数构建记录。
let holiday = fillInRequest "Funk" <| DateTime(2020,12,31)
将代码发送给 FSI 时注意到一些有趣的事情。
val holiday : VacationRequest = {Name = "Funk"; Date = 31/12/2020 12:00:00 ; Approval = null;}
老板然后可以更新请求(创建 新 记录)
let approvedHoliday =
{holiday with Approval = Some "boss' name"}
val approvedHoliday : VacationRequest = {Name = "Funk"; Date = 31/12/2020 12:00:00 ; Approval = Some "boss' name";}
或原样发回
let betterLuckNextTime = holiday