Records
Record pitfalls and extensions: field names are top-level functions, so two records in one module can't share a field name without DuplicateRecordFields. Use OverloadedRecordDot (ana.name, GHC 9.2+) and NoFieldSelectors for more conventional record syntax. Accessors on sum types with different fields are partial; avoid that design.
data Person = Person
{ name :: String
, age :: Int
, email :: Maybe String
}
deriving (Show, Eq)
ana :: Person
ana = Person {name = "Ana", age = 30, email = Just "ana@example.com"}
bo :: Person
bo = Person "Bo" 25 Nothing -- positional construction also works
-- Field accessors are functions
-- name ana == "Ana"
-- Record update syntax (creates a new value)
olderAna :: Person
olderAna = ana {age = 31}
-- Pattern matching on records
greet :: Person -> String
greet Person {name = n, age = a} = n ++ " is " ++ show a
{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}
greet2 :: Person -> String
greet2 Person {..} = name ++ " (" ++ show age ++ ")" -- RecordWildCards
greet3 :: Person -> String
greet3 Person {name, age} = name ++ " " ++ show age -- NamedFieldPuns