-- An enumeration type
data PrimaryColor = Red
| Green
| Blue
*Main> :type Red
Red :: PrimaryColor
-- A discrimated union type
data MyIntList = Empty
| List Int MyIntList
deriving Show
*Main> :t Empty
Empty :: MyIntList
*Main> :t List 3 Empty
List 3 Empty :: MyIntList
Note that MyIntList is a recursively defined data type because it uses itself in the definition. This type of structural type recursion is just the same as calling a function recursively. As long as we have a base case (in this case
Empty
then we can easily build the structure. The way an instance of a type was constructed is available at runtime. Pattern matching is a way of breaking down a value into constiuent parts. For example, we could (but should not!) write a function to calculate the length of the MyIntList
type as follows:
myIntListLength :: MyIntList -> Integer
myIntListLength Empty = 0
myIntListLength (List _ rest) = 1 + myIntListLength rest
There's a good talk about the use of OCaml for financial trading systems available here. Amongst other things the strengths of the pattern matching idea are discussed. In a large system you can change a type, recompile and find out any redundant, missed or impossible cases. In OCaml this seems to be on by default, with GHC you need to use the "-fwarn-incomplete-patterns" option.
Record syntax is an alternative way of specifying a type such that parameters can be referred to be name.
data Person = Person {
firstName :: String
, lastName :: String
, title :: String
} deriving (Show)
-- Normal way of creating a Person
*Main> Person "Bill" "Bob" "Mr"
Person {firstName = "Bill", lastName = "Bob", title = "Mr"}
-- Record syntax allows you to specify parameters by name
-- and therefore order does not matter
*Main> Person { title="Mr", firstName="bill", lastName="bob" }
Person {firstName = "bill", lastName = "bob", title = "Mr"}
The
MyIntList
type is very poor. It's only defined for lists of type integer. We can improve this by making it a parametrized type. In the example below we defined MyList
for any type a.
data MyList a = Empty
| MyList a (MyList a)
deriving (Show)
-- MyList of numbers
*Main> :t MyList 3 (MyList 4 Empty)
MyList 3 (MyList 4 Empty) :: (Num t) => MyList t
-- MyList of characters
*Main> :t MyList 'a' (MyList 'b' Empty)
MyList 'a' (MyList 'b' Empty) :: MyList Char
You can have multiple parametrized types. For example, if we wanted to create a list that alternated types, we could:
data MyOddList a b = OddEmpty
| MyOddList a (MyOddList b a)
deriving (Show)
*Main> :t MyOddList 3 (MyOddList 'c' (MyOddList 4 OddEmpty))
MyOddList 3 (MyOddList 'c' (MyOddList 4 OddEmpty)) :: (Num t) => MyOddList t Char