Josh Bohde

Pattern Matching on Records in Haskell

Posted by Josh Bohde

If you've ever written a function that pattern matches on a record, and then added a field to that record, you've seen a message like the following:

The constructorUsershould have 4 arguments, but has been given 3
In the pattern: User name _ (Just favoriteFood)
In an equation forgreet:
greet (User name _ (Just favoriteFood))
= "Hey, "
++ show name ++ "! Have some " ++ show favoriteFood ++ "!"

Dealing with this just busy work: every function that pattern matches on our changed record, we need to add yet another '_' to. We can avoid this by just using record syntax in our pattern matching:

greet :: User -> String
greet User{name=name, favoriteFood=Just food} = "Hey, " ++ show name
++ "! Have some " ++ show food ++ "!"
greet User{name=name} = "Hello, " ++ show name ++ "."

Now, whenever we add or delete a field we're not using in greet, our code will still compile.

If we want to go ever further, we can use Record Puns by enabling the NamedFieldPuns extension. This lets us remove the name=name part, making our function look like this:

greet :: User -> String
greet User{name, favoriteFood=Just food} = "Hey, " ++ show name
++ "! Have some " ++ show food ++ "!"
greet User{name} = "Hello, " ++ show name ++ "."

By doing this by default, we can eliminate a source of boring work and focus on more interesting things.