F#记录:危险,仅限于有限使用或使用良好的功能?

Pro*_*ool 7 f# record pattern-matching

所以我已经记录在我的F#旅程中,起初它们看起来相当危险.起初这看起来很聪明:

type Card = { Name  : string;
          Phone : string;
          Ok    : bool }

let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Run Code Online (Sandbox Code Playgroud)

cardA与卡匹配的想法.更不用说这里简化的模式匹配:

let withTrueOk =
  list 
  |> Seq.filter
    (function 
      | { Ok = true} -> true
      | _ -> false
  )
Run Code Online (Sandbox Code Playgroud)

问题是:

type Card = { Name  : string;
          Phone : string;
          Ok    : bool }

type CardTwo = { Name  : string;
          Phone : string;
          Ok    : bool }

let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Run Code Online (Sandbox Code Playgroud)

cardA现在属于CardTwo类型,我猜这与F#按顺序运行一切有关.

现在这可能是一个不可能的情况,因为可能永远不会有相同的签名承担两种类型的机会,但这是一种可能性.

录制的东西只有有限的用途,还是我只是在想这个?

svi*_*ick 19

它们并不危险,它们不仅限于使用.

我认为很少有两种类型的成员具有相同的成员.但是如果遇到这种情况,您可以限定要使用的记录类型:

let cardA = { Card.Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Run Code Online (Sandbox Code Playgroud)

记录对于创建(大多数)不可变数据结构非常有用.而且,只需更改一些字段即可轻松创建副本的事实也很棒:

let cardB = { cardA with Ok = true }
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 12

我同意,记录字段作为封闭模块/命名空间的成员起初看起来很奇怪,它们来自更传统的OO语言.但是F#在这里提供了相当大的灵活性.我想你会发现只有做作的情况会导致问题,例如两个记录

  1. 是相同的
  2. 有一个子集/超集关系

第一种情况永远不会发生.后者可以通过具有记录A字段的记录B来解决.

您只需要一个字段可以区分两个字段.除此之外,定义可以是相同的.

type Card =
  { Name : string
    Phone: string
    Ok : bool }

type CardTwo =
  { Name : string
    Phone: string
    Age : int }

let card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
let cardTwo = { Name = "Alf" ; Phone = "(206) 555-0157" ; Age = 21 }
Run Code Online (Sandbox Code Playgroud)

模式匹配也非常灵活,因为您只需匹配足够的字段以区别于其他类型.

let readCard card = 
  match card with
  | { Ok = false } -> () //OK
  | { Age = 21 } -> ()   //ERROR: 'card' already inferred as Card, but pattern implies CardTwo
Run Code Online (Sandbox Code Playgroud)

顺便提一下,使用类型注释可以轻松修复您的场景:

let cardA : Card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Run Code Online (Sandbox Code Playgroud)

  • 模式匹配时.您只需匹配足够的字段以区别于其他记录类型.检查我的代码. (2认同)

pad*_*pad 6

为了让您了解F#提供的内容,我只想提一下OCaml中没有完全限定的记录访问者.因此,要区分具有相同字段的记录类型,必须将它们放入子模块并使用模块前缀引用它们.

所以你在F#中的情况要好得多.使用记录访问器可以快速解决类似记录类型之间的任何歧义:

type Card = { Name: string;
          Phone: string;
          Ok: bool }

type CardSmall = { Address: string;
          Ok: bool }

let withTrueOk list =
  list 
  |> Seq.filter (function 
                 | { Card.Ok = true} -> true (* ambiguity could happen here *)
                 | _ -> false)
Run Code Online (Sandbox Code Playgroud)

而且,F#记录完全没有限制.它提供了很多开箱即用的好功能,包括模式匹配,默认不变性,结构相等等.

  • 如果您使用相同的字段定义两个记录类型,则可以通过使用`[<RequireQualifiedAccess>]`注释类型来防止不合格的访问(以及固有的歧义). (9认同)