With the keyword
Data, we can define new types rather than just synonyms for already existing ones. Types declared with
dataare called algebraic, referencing "sum" and "product". In data types, "sum" means alteration (
A | B, meaning
Bbut not both), and "product" means combination (
A B, meaning
For example, we can create a type that represents a card suit:
data Suit = Hearts
Suitis the type constructor in this definition, and the values (
Clubs) are data constructors. It states that the new type
Suitcan have one of the four values (the
|symbol stands for "or"). This means that
Suitis a sum type, which is any type that has multiple possible representations. Another example of a sum type would be
Boolwhich can be either
data Bool = True | False
Data constructors can have zero or more arguments.
Hearts | Diamondsand
True | Falseare examples of data constructors that have zero arguments - nullary data constructors. Likewise, type constructors can have zero or more arguments.
Boolare examples of nullary type constructors.
data ConfigError =
ConfigErroris a nullary type constructor, and its data constructors are
ConfigErrorFileNotFound FilePath, and
ConfigErrorFileNotFound FilePathis a unary data constructor meaning that it actually holds some data, in this case of type
FilePath. Nullary type constructors we have seen so far contain no data aside from their names.
Both the type name and its constructors must begin with a capital letter, and constructors must be unique to that type, i.e. the same constructor cannot be defined in more than one type. Once a new type is defined, it can be used in functions just like any other data type in Haskell. To illustrate, a very simple function to get the card suit in a string format using pattern matching would be:
suitStr :: Suit -> String
suitStr Hearts = "... of hearts."
suitStr Diamonds = "... of diamonds."
suitStr Spades = "... of spades."
suitStr Clubs = "... of clubs."
ghci> suitStr Hearts
"... of hearts."