Records
| Declare a record | |
| Accessors are functions | |
| Record update | |
| The duplicate name problem |
Declare a record
Curly braces after the constructor give each field a name. The type is still an ordinary product type; the names are an addition, not a different kind of data:
data Person = Person
{ name :: String
, age :: Int
, active :: Bool
} deriving (Show, Eq)
ada = Person { name = "Ada", age = 36, active = True }
Construction by name is order independent, which matters as soon as two fields share a type. Person { age = 36, name = "Ada", active = True } is the same value.
Accessors are functions
Each field name becomes a function from the record to the field. That makes accessors usable wherever a function is expected:
name ada -- "Ada"
map age [ada] -- [36]
sortOn age people -- sort by a field
The last line is the idiom worth remembering. Sorting a list of records by one field needs no lambda, because the accessor already is the projection.
Record update
Copying a record with one field changed has its own syntax. The original is untouched, because nothing is mutable:
older = ada { age = age ada + 1 }
name older -- "Ada", unchanged
age older -- 37
-- several fields at once
inactive = ada { active = False, age = 40 }
A record update copies the whole record, so it is cheap for a few fields and wasteful for a huge one updated in a tight loop. GHC optimises the common cases, but not all of them.
The duplicate name problem
Field names share one global namespace per module, so two records cannot both have a field called name without an extension. The practical workarounds are prefixes or separate modules:
-- both at once is a compile error
data Person = Person { name :: String }
data Company = Company { name :: String }
Prefix the fields (personName,
companyName) or put each type in its own
module and import it qualified. The
DuplicateRecordFields extension lifts the
restriction at the cost of vaguer error messages.
Next:
newtype and type synonyms
Article author: Arthur Isaev