RecordDotSyntax in Haskell

Posted on June 1, 2020 by Riccardo.

I started studying hardcore functional programming by fiddling with PureScript. Back then, using records felt natural since they are first-class citizens in the language:

-- PureScript

type Person = { name :: String, age :: Int }
type Company = { name :: String, owner :: Person }

display :: Company -> String
display c = c.name <> " is run by " <> c.owner.name

nameAfterOwner :: Company -> Company
nameAfterOwner c = c{name = c.owner.name <> "'s Company"}

When I switched to Haskell it took me ages to get used to the quirks of its record syntax:

-- Haskell

data Person = Person { personName :: String, personAge :: Int }
-- ^ Define a new type.
--            ^ Add constructor.
--                     ^ Prefix.
--                                           ^ Prefix.
-- Haskell automatically created
-- Person :: String -> Int -> Person
-- personName :: Person -> String
-- personAge :: Person -> Int

data Company = Company { companyName :: String, companyOwner :: Person }

-- Field names are prefixed, otherwise the program would not
-- compile because of the overlapping `name` getters.

display :: Company -> String
display c = companyName c <> " is run by " <> personName (companyOwner c)

nameAfterOwner :: Company -> Company
nameAfterOwner c = c {companyName = personName (companyOwner c) <> "'s Company"}

Of course some type system or category theory trickery would solve that but it just does not feel right for this specific use-case.

Luckily, the RecordDotSyntax proposal has been accepted. We just need to hang tight while it gets implemented. But if you are like me and cannot wait to start using it, a preprocessor is available today!

In other words, using record-dot-preprocessor, the following code is valid Haskell:

-- Haskell

data Company = Company {name :: String, owner :: Person}
data Person = Person {name :: String, age :: Int}

display :: Company -> String
display c = c.name ++ " is run by " ++ c.owner.name

nameAfterOwner :: Company -> Company
nameAfterOwner c = c{name = c.owner.name ++ "'s Company"}

PureScript has a ton of other niceties. “Differences from Haskell” is a great read on that account.


What’s your experience with records in Haskell? Hyped up about RecordDotSyntax? Let’s talk on Twitter!


Support my work by tweeting this article! 🙏