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

Chr*_*arn 22 f# record discriminated-union

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

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

所以...

  • 是否有明确的理由使用一个而不是另一个?

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

  • 是否有某些功能可用于一个,而不是另一个?

Rob*_*sen 27

把它想象成一个记录是'和',而一个受歧视的联盟是'或'.这是一个字符串和一个int:

type MyRecord = { myString: string
                  myInt: int }
Run Code Online (Sandbox Code Playgroud)

虽然这是一个字符串或int的值,但不是两个:

type MyUnion = | Int of int
               | Str of string
Run Code Online (Sandbox Code Playgroud)

这个虚构的游戏可以在标题屏幕,游戏中,或显示最终得分,但只有其中一个选项.

type Game =
  | Title
  | Ingame of Player * Score * Turn
  | Endgame of Score
Run Code Online (Sandbox Code Playgroud)


Mis*_*hor 11

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

type User = { Username : string; IsActive : bool }

type Body = { 
    Position : Vector2<double<m>>
    Mass : double<kg>
    Velocity : Vector2<double<m/s>> 
}
Run Code Online (Sandbox Code Playgroud)

使用有区别的联合(称为和类型)来表示可以枚举的数据可能值.例如:

type NatNumber =
| One
| Two
| Three
...

type UserStatus =
| Inactive
| Active
| Disabled

type OperationResult<'T> =
| Success of 'T
| Failure of string
Run Code Online (Sandbox Code Playgroud)

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

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

type OperationResult<'T> = { 
    HasSucceeded : bool
    ResultValue : 'T
    ErrorMessage : string
}
Run Code Online (Sandbox Code Playgroud)

但是如果操作失败,那就ResultValue没有意义了.因此,此类型的区分联合版本上的模式匹配将如下所示:

match result with
| Success resultValue -> ...
| Failure errorMessage -> ...
Run Code Online (Sandbox Code Playgroud)

如果你模式匹配我们的操作类型的记录类型版本,它将没有多大意义:

match result with
| { HasSucceeded = true; ResultValue = resultValue; ErrorMessage = _ } -> ...
| { HasSucceeded = false; ErrorMessage = errorMessage; ResultValue = _ } -> ...
Run Code Online (Sandbox Code Playgroud)

它看起来冗长而笨拙,也可能效率低下.我想当你有这种感觉时,这可能暗示你正在使用错误的工具完成任务.


pad*_*pad 7

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

  • 默认不可变
  • 结构平等默认
  • 易于模式匹配
  • 等等

受歧视的联盟编码替代方案,例如

type Expr =
    | Num of int
    | Var of int 
    | Add of Expr * Expr 
    | Sub of Expr * Expr
Run Code Online (Sandbox Code Playgroud)

杜上述被读出如下:表达式是任一的整数,或者一个变量,加成两个表达式两个表达式之间减法.这些情况不可能同时发生.

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

type Name =
    { FirstName : string;
      MiddleName : string option;
      LastName : string }
Run Code Online (Sandbox Code Playgroud)

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

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

另一方面,受歧视的工会被用来模拟案件之间的替代和相互排斥关系.