在其他几个与State相关的类型中,我的代码中有以下记录类型:
type SubmittedSuggestionData = { SuggestionId : Guid SuggestionText : string Originator : User ParentCategory : Category SubmissionDate : DateTime } type ApprovedSuggestionData = { SuggestionId : Guid SuggestionText : string Originator : User ParentCategory : Category SubmissionDate : DateTime ApprovalDate : DateTime }
然后将这些内容输入以下内容:
type Suggestion = | SubmittedSuggestion of SubmittedSuggestionData | ApprovedSuggestion of ApprovedSuggestionData
这使我能够使用状态机样式模式来执行依赖于State的特定业务逻辑.(这种方法取自:http://fsharpforfunandprofit.com/posts/designing-with-types-representing-states/)
我有一个函数,以最简单的形式,将a更改SubmittedSuggestion
为ApprovedSuggestion
:
let ApproveSuggestion suggestion = match suggestion with | SubmittedSuggestion suggestion -> ApprovedSuggestion {}
这个功能目前还不完整,因为我正在努力理解的是当Suggestion从Subved变为Approved时,如何将传入的属性复制suggestion
到新创建的ApprovedSuggestion
同时还填充新属性ApprovalDate
?
我想如果我做了类似的事情会有效:
let ApproveSuggestion suggestion = match suggestion with | SubmittedSuggestion {SuggestionId = suggestionId; SuggestionText = suggestionText; Originator = originator; ParentCategory = category; SubmissionDate = submissionDate} -> ApprovedSuggestion {SuggestionId = suggestionId; SuggestionText = suggestionText; Originator = originator; ParentCategory = category; SubmissionDate = submissionDate; ApprovalDate = DateTime.UtcNow}
但这对我来说非常可怕.
是否有一种更清洁,更简洁的方法来获得相同的结果?我已经尝试使用该with
关键字,但它没有编译.
谢谢
如果类型之间存在大的重叠,那么考虑分解通常是个好主意.例如,类型可能如下所示:
type SuggestionData = { SuggestionId : Guid SuggestionText : string Originator : User ParentCategory : Category SubmissionDate : DateTime } type ApprovedSuggestionData = { Suggestion : SuggestionData ApprovalDate : DateTime }
根据用途和类型之间的差异,人们还可以考虑仅在受歧视的联合中使用已批准的类型,完全跳过第二种类型:
type Suggestion = | SubmittedSuggestion of SuggestionData | ApprovedSuggestion of SuggestionData * approvalDate : DateTime
反对这种分解的一个常见论点是,对层次结构深层次类型成员的访问变得更加冗长,例如approvedSuggestionData.Suggestion.Originator
.虽然这是真的,但如果冗长变得令人讨厌,则可以使用属性来转发常用的成员成员,并且应该权衡任何缺点:类型中的代码重复较少,以及更细粒度类型提供的任何操作都可以可以从组合类型中获得.
从未经批准的建议和批准日期轻松构建批准建议的能力是一个有用的案例.但可能会有更多:例如,有一项操作可以验证所有建议的用户和类别,无论是否批准.如果持有批准和未批准建议的成员Originator
和ParentCategory
成员不相关,则需要复制获取这些建议的代码.(或者需要创建一个通用接口.)