Pattern Matching on Records in Haskell
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 constructor ‘User’ should have 4 arguments, but has been given 3
• In the pattern: User name _ (Just favoriteFood)
In an equation for ‘greet’:
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.