何时在F#中使用判别联盟vs记录类型

时间:2013-06-25 07:50:52

标签: f# record discriminated-union

在尝试使用复杂的示例之前,我试图了解F#的基础知识。我正在学习的材料引入了Discriminate Unions和Record类型。我已经审查了两者的材料,但我仍然不清楚为什么我们会使用其中一个。

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

因此...

  • 有明确的理由使用其中一个吗?

  • 是否存在某些适用的规范案例?

  • 是否有某些功能可用,但不是 其他

4 个答案:

答案 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

请注意,区分联合值的可能值也是互斥的 - 操作的结果可以是SuccessFailure,但不能同时为两者。

您可以使用记录类型对操作结果进行编码,如下所示:

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实现,尽管编译器隐藏了这个细节。