Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructing and deconstructing records

Tags:

f#

The msdn page documenting Records (F#) details record expressions for record construction and record patterns for deconstruction, the latter without naming them as such. Here's an example which uses both techniques for an arithmetic operator:

// Simple two-dimensional generic vector defintion
type 'a UV =
    { U : 'a; V : 'a }
    static member inline (+) ({ U = au; V = av }, { U = bu; V = bv }) =
        { U = au + bu; V = av + bv }

This appears unwieldy and not very readable. For deconstruction, there are dot-notation or functions as alternatives. Since the dot-notation operator has a special dispensation in section 8.4.2 Name Resolution and Record Field Labels of the spec (an expression’s type may be inferred from a record label), there's normally no need to annotate. Accessor functions like let u { U = u } = u wouldn't give us any advantages then.

For construction, I think a case can be made for a function as record constructor. Access to the original constructor might even be restricted:

type 'a UV =
    internal { U : 'a; V : 'a }
let uv u v = { U = u; V = v }
type 'a UV with
    static member inline (+) (a, b) =
        uv (a.U + b.U) (a.V + b.V)

Is this an idiomatic thing to do? How to package such functions in modules and handle namespace issues?

like image 528
kaefer Avatar asked Jan 06 '15 17:01

kaefer


1 Answers

Short answer: I don't think there is a general convention here at the moment so it will be a personal decision in the end.

To summarise what you get for free with records in F# is:

  • Construct: { U = u; V = v } (bracket-notation)

  • Deconstruct: let u = record.u (dot-notation) and let {U = u} = record (pattern matching)

  • Update: {record with U = u} (bracket-notation)

But you don't get first class functions for free, if you want you can code them by hand.

The following is what I would personally use as convention:

A static member New with curried arguments for record construction.

For update and deconstruction I would use some kind of Lenses abstraction.

Here's an example of the code I would have to add by hand:

// Somewhere as a top level definition or in a base library
type Lens<'T,'U> = {Get: 'T -> 'U; Set: 'U -> 'T -> 'T } with
  member l.Update f a = l.Set (f (l.Get a)) a


type UV<'a> = {U : 'a; V : 'a } with
// add these static members to your records
  static member New u v : UV<'a> = {U = u; V = v}
  static member u = {Get = (fun (x: UV<'a>) -> x.U); Set = fun t x -> {x with U = t}}
  static member v = {Get = (fun (x: UV<'a>) -> x.V); Set = fun t x -> {x with V = t}}


let uvRecord  = UV.New 10 20
let u         = UV.u.Get uvRecord
let uvRecord1 = UV.u.Set (u+1) uvRecord
let uvRecord2 = UV.u.Update ((+)1) uvRecord

This way I would have first class functions for construction, deconstruction but also for updates plus other very interesting Lenses properties as you can read in this post.

UPDATE (in response to your comments)

Of course they can be defined later, what does it change? The same applies for the New constructor, it can be defined later but that's actually a good thing. The accessor functions you defined can also be defined later, indeed any first-class getter, setter or updater value can be defined later.

Anyway the answer to your question is "no, there are no conventions" the rest it's a personal decision, which would be my decision and also many Haskellers are pushing to get some kind of automatic Lenses for Haskell records.

Why would I decide to go this way? Because in terms of lines of code the effort of adding a simple accessor function is almost the same as adding a get-Lens, so for the same price I get more functionality.

If you are not happy with the Lenses discussion please tell me, I can delete it and leave the short answer, or I can delete the whole answer too if it's confusing instead of clarifying.

Or may be I misunderstood your question, for me your question was about which convention is generally used to add first-class constructors, getters and setters values for records.

Composition is not the only advantage of Lenses, you can do many things, keep reading about them, they provide a very interesting abstraction and not only restricted to records.

like image 82
Gus Avatar answered Nov 11 '22 20:11

Gus