我正在学习F#,我想知道我是否正确地接近它。我创建了一个包含6个标签的记录类型,但它可以很容易地增长到10个以上的标签。 我应该如何设计其构造函数?由于记录值是不可变的,我想我应该一次创建。构造函数中的每个参数在构造值时映射到标签。但是,这感觉不对。
例如:
module Deal =
open System
type T = {
Title : string;
Description: string;
NumberOfVotes: int;
NumberOfComments: int;
DateCreated: DateTime;
ImageUrl: string;
}
let Create (title:string) (desc:string) (numberOfVotes:int) (numberOfComments:int) (dateCreated: DateTime) (imageUrl:string) =
{
Title = title;
Description = desc;
NumberOfVotes = numberOfVotes;
NumberOfComments = numberOfComments;
DateCreated = dateCreated;
ImageUrl = imageUrl;
}
我不认为随着更多标签添加到记录中,构造函数可以很好地扩展。
有更好的解决方案吗?
答案 0 :(得分:4)
在 F#for fun and profit 示例中,存储的电子邮件地址是一个字符串,它是一个包装类型(使用区别联合)。您似乎没有包装类型。这就是我如何根据您的情况调整示例:
open System
type Deal =
{ Title : string; Description: string; NumberOfVotes: int
NumberOfComments: int; DateCreated: DateTime; ImageUrl: string }
module Deal =
type ValidDeal =
private ValidDeal of Deal // Note the `private` on the case constructor
let isValid deal = true // Add implementation
let create deal =
if isValid deal then Some (ValidDeal deal)
else None
let (|ValidDeal|) (ValidDeal deal) = deal // This is an active pattern
open Deal
let f (ValidDeal d) = d // Use the active pattern to access the Deal itself.
let g d = ValidDeal d // Compile error. The ValidDeal union case constructor is private
请注意,ValidDeal
union case构造函数对Deal
模块是私有的,该模块还包含isValid
函数。因此,该模块可以完全控制交易的验证。此模块之外的任何代码都必须使用Deal.create
来创建有效的交易,如果任何函数收到ValidDeal
,则有一些编译时保证有效。
答案 1 :(得分:3)
记录构造通常不能很好地扩展:您需要为每个构造表达式添加其他字段。如果您可以为其他字段指定合理的默认值,则带有可选参数的构造函数可能有所帮助。
open System
type Deal =
{ Title : string; Description: string; NumberOfVotes: int
NumberOfComments: int; DateCreated: DateTime; ImageUrl: string } with
static member Create
( ?title : string,
?desc : string,
?numberOfVotes : int,
?numberOfComments : int,
?dateCreated: DateTime,
?imageUrl : string ) =
{ Title = defaultArg title "No title"
Description = defaultArg desc "No description"
NumberOfVotes = defaultArg numberOfVotes 0
NumberOfComments = defaultArg numberOfComments 0
DateCreated = defaultArg dateCreated DateTime.Now
ImageUrl = defaultArg imageUrl "Default.jpg" }
虽然是更多的样板,但可以引入其他字段而不会影响构造函数调用站点。有两种方法可以提供特定的参数,可以是命名参数,也可以是F#的复制和更新记录表达式。
Deal.Create(title = "My Deal") // named argument
{ Deal.Create() with NumberOfVotes = 42 } // copy-and-update