Record Types

Dear Computer

Chapter 9: Types Revisited

Record Types

Haskell's data command is versatile. We use it to define plain enums. We use it define data-bearing tagged unions. We use it to define really messy tuple types like this Friend type:

Haskell
--                   name   bday email  phone  address  city   state  zip
data Friend = Friend String Date String String String   String String String

Unlike the previous types we've defined in Haskell, Friend has only a single variant. We still need to name the variant, even though there's only one. Here, both the type itself and the variant are named Friend. That's legal.

Suppose we want a function to yield the name of a Friend. The only way to extract the friend's name is through destructuring. So we write this accessor function:

Haskell
name :: Friend -> String
name (Friend nombre _ _ _ _ _ _ _) = nombre

All but one of the fields is irrelevant, and they are therefore discarded with _.

Then we starting adding accessors for the other fields:

Haskell
birthday :: Friend -> Date
birthday (Friend _ date _ _ _ _ _ _) = date

email :: Friend -> String
email (Friend _ _ address _ _ _ _ _) = address

-- ...

But we give up, because this is too much work. Tuples of high arity are cumbersome. The Haskell designers agree, and that's why they also allow us to give the fields names through the language's record syntax. This revised definition ascribes names to the fields of Friend:

Haskell
data Friend = Friend { name :: String
                     , birthday :: Date
                     , email :: String
                     , phone :: String
                     , address :: String
                     , city :: String
                     , state :: String
                     , zipCode :: String } deriving Show

Placing the separating comma in front of a field rather than at the end of the preceding line is typical in Haskell programs.

Now that the fields are named, a Friend is no longer a tuple but a record. The Haskell compiler automatically adds accessors for the fields of a record type. This interaction constructs a Friend and then accesses its fields through these automatically defined functions:

ghci
> friend = Friend "Jasmine" (Date 2003 12 21) "eg@example.com" "?" "114 Whopper Lane" "Hogbottom" "AK" "41229"
> name friend
Jasmine
> email friend
eg@example.com

Record types are more humane than tuples in a couple of other ways. The constructors accept named parameters, and show lists the data with names, as demonstrated in this GHCI interaction:

ghci
> friend = Friend { name = "Jasmine", birthday = Date 2003 12 21, email = "eg@example.com", phone = "?", address = "114  Whopper Lane", city = "Hogbottom", state = "AK", zipCode = "41229" }
> friend
Friend {name = "Jasmine", birthday = Date 2003 12 21, email = "eg@example.com", phone = "?", address = "114  Whopper Lane", city = "Hogbottom", state = "AK", zipCode = "41229"}
← TypeclassesGeneric Types →