在尝试使用复杂的示例之前,我试图了解F#的基础知识。我正在学习的材料引入了Discriminate Unions和Record类型。我已经审查了两者的材料,但我仍然不清楚为什么我们会使用其中一个。
我创建的大多数玩具示例似乎都可以在两者中实现。记录似乎与我认为的C#中的对象非常接近,但我试图避免依赖映射到c#作为理解F#的方法
因此...
有明确的理由使用其中一个吗?
是否存在某些适用的规范案例?
是否有某些功能可用,但不是 其他
答案 0 :(得分:27)
将其视为记录是'和',而受歧视的联盟是'或'。 这是一个字符串和一个int:
type MyRecord = { myString: string
myInt: int }
虽然这是一个字符串或int值,但不是两者:
type MyUnion = | Int of int
| Str of string
这个虚构的游戏可以在标题屏幕,游戏中,或显示最终得分,但只有其中一个选项。
type Game =
| Title
| Ingame of Player * Score * Turn
| Endgame of Score
答案 1 :(得分:11)
对复杂数据使用记录(在函数式编程理论中称为产品类型),这些数据由多个属性描述,如数据库记录或某些模型实体:
type User = { Username : string; IsActive : bool }
type Body = {
Position : Vector2<double<m>>
Mass : double<kg>
Velocity : Vector2<double<m/s>>
}
对可以枚举的数据可能值使用有区别的联合(称为和类型)。例如:
type NatNumber =
| One
| Two
| Three
...
type UserStatus =
| Inactive
| Active
| Disabled
type OperationResult<'T> =
| Success of 'T
| Failure of string
请注意,区分联合值的可能值也是互斥的 - 操作的结果可以是Success
或Failure
,但不能同时为两者。
您可以使用记录类型对操作结果进行编码,如下所示:
type OperationResult<'T> = {
HasSucceeded : bool
ResultValue : 'T
ErrorMessage : string
}
但是如果操作失败,那么ResultValue
没有意义。因此,此类型的区分联合版本上的模式匹配将如下所示:
match result with
| Success resultValue -> ...
| Failure errorMessage -> ...
如果你模式匹配我们的操作类型的记录类型版本,那就没那么明白了:
match result with
| { HasSucceeded = true; ResultValue = resultValue; ErrorMessage = _ } -> ...
| { HasSucceeded = false; ErrorMessage = errorMessage; ResultValue = _ } -> ...
看起来冗长而笨拙,也可能效率低下。我觉得当你有这样的感觉时,这可能暗示你正在使用错误的工具来完成任务。
答案 2 :(得分:7)
如果您来自C#,您可以将记录理解为带有附加值的密封类:
受歧视的联合编码替代,例如
type Expr =
| Num of int
| Var of int
| Add of Expr * Expr
| Sub of Expr * Expr
上面的DU读取如下:表达式是 整数,或变量,或两个表达式的加法< em>或两个表达式之间的减法。这些情况不可能同时发生。
您需要所有字段来构建记录。您也可以在记录中使用DU,反之亦然
type Name =
{ FirstName : string;
MiddleName : string option;
LastName : string }
上面的示例显示中间名是可选的。
在F#中,您经常使用元组或记录开始建模数据。当需要高级功能时,您可以将它们移动到类。
另一方面,受歧视的联盟用于模拟替代方案和案例之间的互斥关系。
答案 3 :(得分:2)
理解DU的一种(略有缺陷的)方式是将其视为一个奇特的C#“联合”,而记录更像是一个普通的对象(具有多个独立的字段)。
查看DU的另一种方法是将DU视为两级类层次结构,其中顶部DU类型是抽象基类,DU的情况是子类。这个视图实际上接近实际的.NET实现,尽管编译器隐藏了这个细节。