Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Records: Dangerous, only for limited use, or well used functionality?

So have gotten to record in my F# journey and at first they seem rather dangerous. At first this seemed clever:

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

let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }

The idea that the cardA is patten matched with Card. Not to mention the simplified pattern matching here:

let withTrueOk =
  list 
  |> Seq.filter
    (function 
      | { Ok = true} -> true
      | _ -> false
  )

Problem is:

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 }

cardA is now of CardTwo type which I am guessing has to do with F# running everything in order.

Now this might be an impossible situation since there may never be a chance of the same signature taking on two type, but it is a possibility.

Is recording something that has only limited use or am I just over thinking this one?

like image 754
Programmin Tool Avatar asked Jan 11 '12 17:01

Programmin Tool


3 Answers

They are not dangerous and they are not only for limited use.

I think it's very rare that you would have two types with the same members. But if you do encounter that situation, you can qualify the record type you want to use:

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

Records are very useful for creating (mostly) immutable data structures. And the fact that you can easily create a copy with just some fields changed is great too:

let cardB = { cardA with Ok = true }
like image 160
svick Avatar answered Apr 29 '23 11:04

svick


I agree, record fields as members of the enclosing module/namespace seems odd at first coming from more traditional OO languages. But F# provides a fair amount of flexibility here. I think you'll find only contrived circumstances cause problems, such as two records that

  1. are identical
  2. have a subset/superset relationship

The first case should never happen. The latter could be solved by record B having a field of record A.

You only need one field to be different for the two to be distinguishable. Other than that, the definitions can be the same.

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 }

Pattern matching is also quite flexible as you only need to match on enough fields to distinguish it from other types.

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

Incidentally, your scenario is easily fixed with a type annotation:

let cardA : Card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
like image 37
Daniel Avatar answered Apr 29 '23 12:04

Daniel


In order that you appreciate what F# provides, I just want to mention that there is no fully qualified accessor for records in OCaml. Therefore, to distinguish between record types with the same fields, you have to put them into submodules and reference them using module prefixes.

So your situation in F# is much better. Any ambiguity between similar record types could be resolved quickly using record accessor:

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)

Moreover, F# record is not limited at all. It provides a lot of nice features out-of-the-box including pattern matching, default immutability, structural equality, etc.

like image 25
pad Avatar answered Apr 29 '23 12:04

pad