A record is a Haskell data value with named fields. Field names document what each part means and automatically create field-selector functions, avoiding the unclear positional access of tuples. A CSV table, for example, can name its optional header and its columns directly. section 7.1.1
Listing 7.1 A data type for CSV file contents using the record syntax
module Csv.Types where
import qualified Data.Text as T #1
type Column = [T.Text]
data Csv = Csv #2
{ csvHeader :: Maybe [T.Text], #3
csvColumns :: [Column]
}
deriving (Show) #4
This CSV type names its header and columns; csvHeader and csvColumns are generated functions for retrieving those fields. section 7.1.1
Records support several practical operations:
- Construct a value with named fields, or use the ordinary positional constructor form when appropriate. section 7.1.1
- Select a field with its generated function, such as csvColumns. section 7.1.1
- Update selected fields while retaining the rest of a record. section 7.1.1
- Pattern-match on only the fields needed; NamedFieldPuns and RecordWildCards reduce repeated field names for larger records. section 7.3
- Hide a constructor through module exports so callers cannot create values that violate required invariants. section 7.1.1
Computing CSV dimensions
With RecordWildCards, Csv {..} brings csvColumns into scope. numberOfRows examines the first column to find its length, while numberOfColumns counts csvColumns. This relies on the CSV invariant that every column has the same number of fields. section 7.3
Avoid partial selectors
A shared field such as start can safely select from either Tone or Silence. A field present only on Tone, such as freq, is partial and fails on Silence. Enable GHC's partial-field warning, or use NoFieldSelectors and define safe access functions explicitly. section 10.3.1











