F# — Record types vs classes

In F#, you can defined record types which dif­fer from tuples and dis­crim­i­nat­ed unions in that they allow you to orga­nize val­ues into a type and name those val­ues through fields:

image

Looks like a cut-down ver­sion of a stan­dard .Net class with a cou­ple of prop­er­ties? Yes they do, but records offer sev­er­al advan­tages over the .Net class­es.

Type Inference

One of the biggest advan­tage records offer is that it works seam­less­ly with F#‘s type infer­ence sys­tem. Where­as class­es must be anno­tat­ed in order to be used, record types can be inferred by the fields you access!

Con­sid­er this sim­ple func­tion:

image

and see what its func­tion sig­na­ture reads:

image

Mag­ic! Well, with a caveat :-P As you can imag­ine, if you define a sec­ond record type which con­tains the same set of fields then it might just con­fus­es the com­pil­er a lit­tle. For instance, if you define a record type for employ­ees:

image

The func­tion sig­na­ture for the print­Per­son func­tion will become:

image

So to tell the print­Per­son func­tion to use the Per­son record instead, you’ll need to anno­tate the para­me­ter p:

image

Immutability

Unlike class­es, record fields are immutable by default. This immutabil­i­ty by default approach (zero side effect) is inline with every­thing else in F# and in func­tion­al lan­guages in gen­er­al, and offers a degree of safe­ty guar­an­tee.

How­ev­er, whether immutabil­i­ty is an advan­tage very much depends on your use case, Eric Lip­pert made an excel­lent argu­ment here that the lack of side effects and immutable data struc­tures func­tion lan­guages offer are both advan­tages and dis­ad­van­tages.

For­tu­nate­ly, with F# record types, it’s pos­si­ble to change the field val­ues, either by using the copy and update record expres­sion:

image

or you can explic­it­ly spec­i­fy that a field should be muta­ble:

image

then you are allowed to update the val­ue of the muta­ble fields:

image

No Inheritance

Records can­not be inher­it­ed, which gives you a future guar­an­tee of safe­ty but then again you can equal­ly make argu­ments against it, anoth­er post anoth­er day maybe..

Pattern Matching

Records can be used as part of stan­dard pat­tern match­ing where­as class­es can­not with­out resort­ing to active pat­terns or with when guards.

This is impor­tant, as you’re like­ly to be using pat­tern match­ing every­where in your code and frankly, why wouldn’t you? It’s so awe­some ;-) You can mach the struc­ture of records and not every record field needs to be a part of the pat­tern match:

image

Equal­ly you can cap­ture val­ues and match lit­er­al:

image

Structural Equality Semantics

Stan­dard .Net class­es have ref­er­ence equal­i­ty seman­tics (two ref­er­ence type vari­ables are deemed equal if they point to the same loca­tion in mem­o­ry), but records have struc­tur­al equal­i­ty seman­tics (two val­ues are deemed equal if their fields are equal):

image

In this respect, records are the same as .Net structs, which also struc­tur­al equal­i­ty seman­tics and can­not be inher­it­ed.

Update 27/09/2011:

One thing I for­got to men­tion is that whilst you can define a record type with just the list of fields, you can also add meth­ods and prop­er­ties using the mem­ber key­word:

image