Gentle Introduction to Haskell 98, Online Supplement Part 3 Covers Section 2.1 Section: 2.2 User-Defined Types > module Part3() where The type Bool is already defined in the Prelude so there is no need to define it here. > data Color = Red | Green | Blue | Indigo | Violet deriving Show The `deriving Show' is necessary if you want to print a Color value. You can now evaluate these expressions. > e1 :: Color > e1 = Red > e2 :: [Color] > e2 = [Red,Blue] It is very important to keep the expression language and the type language in Haskell separated. The data declaration above defines the type constructor Color. This is a nullary constructor: it takes no arguments. Color is found ONLY in the type language - it can not be part of an expression. e1 = Color is meaningless. (Actually, Color could be both a data constructor and a type constructor but we'll ignore this possibility for now). On the other hand, Red, Blue, and so on are (nullary) data constructors. They can appear in expressions and in patterns (described later). The declaration e1 :: Blue would also be meaningless. Data constructors can be defined ONLY in a data declaration. In the next example, Point is a type constructor and Pt is a data constructor. Point takes one argument and Pt takes two. A data constructor like Pt is really just an ordinary function except that it can be used in a pattern. Type signatures can not be supplied directly for data constructors; their typing is completely defined by the data declaration. However, data constructors have a signature just like any variable: Pt :: a -> a -> Point a -- Not valid Haskell syntax That is, Pt is a function which takes two arguments with the same arbitrary type and returns a value containing the two argument values. > data Point a = Pt a a deriving Show > e3 :: Point Float > e3 = Pt 2.0 3.0 > e4 :: Point Char > e4 = Pt 'a' 'b' > e5 :: Point (Point Int) > e5 = Pt (Pt 1 2) (Pt 3 4) > -- e6 = Pt 'a' True -- This is a typing error The individual components of a point do not have names. Let's jump ahead a little so that we can write functions using these data types. Data constructors (Red, Blue, ..., and Pt) can be used in patterns. When more than one equation is used to define a function, pattern matching occurs top down. A function to remove red from a list of colors. > removeRed :: [Color] -> [Color] > removeRed [] = [] > removeRed (Red:cs) = removeRed cs > removeRed (c:cs) = c : removeRed cs -- c cannot be Red at this point > e7 :: [Color] > e7 = removeRed [Blue,Red,Green,Red] Pattern matching is capable of testing equality with a specific color. All equations defining a function must share a common type. A definition such as: foo Red = 1 foo (Pt x y) = x would result in a type error since the argument to foo cannot be both a Color and a Point. Similarly, the right hand sides must also share a common type; a definition such as foo Red = Blue foo Blue = Pt Red Red would also result in a type error. Here are a couple of functions defined on points. > dist :: Point Float -> Point Float -> Float > dist (Pt x1 y1) (Pt x2 y2) = sqrt ((x1-x2)^2 + (y1-y2)^2) > midpoint :: Point Float -> Point Float -> Point Float > midpoint (Pt x1 y1) (Pt x2 y2) = Pt ((x1+x2)/2) ((y1+y2)/2) > p1 :: Point Float > p1 = Pt 1.0 1.0 > p2 :: Point Float > p2 = Pt 2.0 2.0 > e8 :: Float > e8 = dist p1 p2 > e9 :: Point Float > e9 = midpoint p1 p2 The only way to take apart a point is to pattern match. That is, the two values which constitute a point must be extracted by matching a pattern containing the Pt data constructor. Much more will be said about pattern matching later. Haskell prints values in the same syntax used in expressions. Thus Pt 1 2 would print as Pt 1 2 (of course, Pt 1 (1+1) would also print as Pt 1 2). Continued in part4.lhs