24

在继续讨论复杂示例之前,我试图弄清 F# 的基础知识。我正在学习的材料介绍了区分联合和记录类型。我已经审查了两者的材料,但我仍然不清楚为什么我们会使用其中一种。

我创建的大多数玩具示例似乎都可以在两者中实现。记录似乎非常接近我认为的 C# 中的对象,但我试图避免依赖映射到 c# 作为理解 F# 的一种方式

所以...

  • 是否有明确的理由使用其中一种?

  • 是否存在某些适用的典型案例?

  • 是否有某些功能在其中一个可用,而另一个则没有?

4

4 回答 4

29

把它想象成一个记录是“和”,而一个有区别的联合是“或”。这是一个字符串和一个 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
于 2013-06-25T08:32:20.840 回答
12

对由多个属性描述的复杂数据使用记录(在函数式编程理论中称为产品类型),例如数据库记录或某些模型实体:

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

请注意,可区分联合值的可能值也是互斥的——操作的结果可以是Successor 或 a 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 = _ } -> ...

它看起来冗长而笨拙,而且可能效率也较低。我认为当你有这样的感觉时,可能暗示你使用了错误的工具来完成任务。

于 2013-06-25T09:28:38.840 回答
8

如果您来自 C#,您可以将记录理解为具有附加值的密封类:

  • 默认不可变
  • 默认情况下结构相等
  • 易于模式匹配
  • 等等

有区别的联合编码替代方案,例如

type Expr =
    | Num of int
    | Var of int 
    | Add of Expr * Expr 
    | Sub of Expr * Expr

上面的DU读法如下:一个表达式要么是一个整数,要么是一个变量,要么是两个表达式的加法,要么是两个表达式之间的减法。这些情况不能同时发生。

您需要所有字段来构建记录。您还可以在记录中使用 DU,反之亦然

type Name =
    { FirstName : string;
      MiddleName : string option;
      LastName : string }

上面的示例显示中间名是可选的。

在 F# 中,您通常开始使用元组或记录对数据进行建模。当需要高级功能时,您可以将它们移动到类中。

另一方面,有区别的联合用于模拟案例之间的替代和互斥关系。

于 2013-06-25T08:47:01.303 回答
3

理解 DU 的一种(稍微有缺陷的)方法是将其视为花哨的 C#“联合”,而记录更像是一个普通对象(具有多个独立字段)。

另一种看待 DU 的方法是将 DU 视为两级类层次结构,其中顶级 DU 类型是抽象基类,DU 的案例是子类。这个视图实际上接近于实际的 .NET 实现,尽管这个细节被编译器隐藏了。

于 2013-06-25T11:20:59.297 回答