Part 19

Record Syntax

If some fields need to be accessed often, it can be convenient to have helper functions for reading those fields. For instance, the type Person might have multiple fields:

data Person = MkPerson String Int String String String deriving Show

A list of persons might look like the following:

people :: [Person]
people = [ MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
            , MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"
            , MkPerson "Mauno Mutikainen" 27 "Turku" "Finland" "Mathematician"
            ]

Suppose that we need to find all engineers from Finland:

query :: [Person] -> [Person]
query [] = []
query ((MkPerson name age town state profession):xs)
    | state == "Finland" && profession == "Engineer" =
        (MkPerson name age town state profession) : query xs
    | otherwise = query xs

Thus,

query people ==> [MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"]

Note that the types of the fields give little information on what is the intended content in those fields. We need to remember in all places in the code that town goes before state and not vice versa.

Haskell has a feature called record syntax that is helpful in these kinds of cases. The datatype Person can be defined as a record:

data Person = MkPerson { name :: String, age :: Int, town :: String, state :: String, profession :: String}
    deriving Show

We can still define values of Person normally, but the Show instance prints the field names for us:

Prelude> MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}

However, we can also define values using record syntax. Note how the fields don’t need to be in any specific order now that they have names.

Prelude> MkPerson {name = "Jane Doe", town = "Houston", profession = "Engineer", state = "Texas", age = 21}
MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}

Most importantly, We get accessor functions for the fields for free:

Prelude> :t profession
profession :: Person -> String
Prelude> profession (MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer")
"Engineer"

We can now rewrite the query function using these accessor functions:

query :: [Person] -> [Person]
query []     = []
query (x:xs)
    | state x == "Finland" && profession x == "Engineer" =
        x : query xs
    | otherwise = query xs

You’ll probably agree that the code looks more pleasant now.

You have reached the end of this section! Continue to the next section:

You can check your current points from the blue blob in the bottom-right corner of the page.