jonnifyfsharp

Dive C1.06.1

Records

A value that has field A and field B at once — named, immutable, compared by its contents.

A record is F#'s product type: it groups a fixed set of named fields into one value. The declaration type Point = { X: float; Y: float } names the type and its two fields; a value is built by naming every field, and F# reads the record type back off those names — no annotation needed. Records are immutable: there is no statement that changes a field in place. To get a changed value you build a new one with the copy-and-update expression { p with Y = 3.0 }, which copies p and replaces the named field, leaving p exactly as it was. And two records are equal when their fields are equal — structural equality comes for free.

C1.06.1 · part 1

Declare it, build it

The declaration lists the fields and their types between braces. To build a value, name every field and give it a value; the order of the fields in the expression does not matter. Because the field names X and Y belong to exactly one declared record, F# infers the type Point without a hint.

type Point = { X: float; Y: float }

let p = { X = 1.0; Y = 2.0 }
// val p : Point = { X = 1.0; Y = 2.0 }

A field is read with dot notation, the same as any .NET member: p.X is 1.0. Nothing about the value can change after this line; the next part shows how to get a different value without mutating this one.

record
a product type: one value holds a fixed set of named fields, all present at once.
{ X = …; Y = … }
a record expression — name every field; F# infers the type from the names.
p.X
field access by name, with dot notation.
immutable
no field can be reassigned in place; a different value is a different record.
C1.06.1 · part 2

Copy-and-update returns a new record

The expression { p with Y = 3.0 } reads "a copy of p, with Y set to 3.0". It produces a new Point; p is untouched. Below, pick a field to change and a new value for it. The original record stays fixed on the left; the copy on the right shows the result, with the changed field highlighted. The equality readout confirms the two are now different values.

{ p with <field> = <value> } · p stays · a new record comes back

p · original X = 1.0 Y = 2.0 unchanged with the copy X = 1.0 Y = 3.0 Y replaced
Field to update
New value
{ p with Y = 3.0 } => { X = 1.0; Y = 3.0 } · p still { X = 1.0; Y = 2.0 } · p = copy : false

Copy-and-update never edits the old record; it returns a new one. The original keeps its value, so a reference held elsewhere never changes underfoot.

The same in FSI: bind the result of { p with Y = 3.0 } to a name, then read p back to confirm it did not move.

let p2 = { p with Y = 3.0 }
// val p2 : Point = { X = 1.0; Y = 3.0 }

p
// val it : Point = { X = 1.0; Y = 2.0 }   (p never changed)
C1.06.1 · part 3

Equality is structural

Two records are equal when their fields are equal — F# generates the comparison from the type, so = works on records out of the box. This is why the copy above is "different from" p: one field differs, so the whole values differ. Two separately built records with the same contents are equal.

let a = { X = 1.0; Y = 2.0 }
let b = { X = 1.0; Y = 2.0 }
a = b
// val it : bool = true    (same contents, equal)

p = p2
// val it : bool = false   (Y differs)
Anonymous records. F# also has anonymous records, written with bars inside the braces: {| X = 1; Y = 2 |}. They need no type declaration — the type is the shape. FSI gives the value above the structural type {| X: int; Y: int |}. They suit a one-off shape, such as the result of a query, where a named record would be overkill.

Product type

A value carries field A and field B and field C — every field present at once.

F# record

{ X: float; Y: float } — named, immutable, copy-and-updated, compared by contents.

Where this leads

A record is the and. The next dive builds the or: a discriminated union, where a value is one of a closed set of cases rather than all the fields at once. Together the two cover every shape a domain needs, and the third dive uses both to make a wrong value impossible to write.

References

Sources

Related in this course