[Work on ch13 John Goerzen **20070815050209] { hunk ./en/ch13-data.xml 22 - - Basic Type Creation + + Creating New Types hunk ./en/ch13-data.xml 25 - To create a new type, we use the &data; keyword. In its most simple, - though probably useless, form, you can create a type like this: + To create a new type, we use the &data; keyword. You can create an + amazing variety of types using &data;. We'll take a look at them, + starting with the most simple, and moving on to more complex types. hunk ./en/ch13-data.xml 29 - &data1.hs:useless; - - This defines a new type called Silly. There is one - type constructor for Silly: - Foo. When you write Foo in your - program, this Foo is a value of type - Silly. You can actually use the same word for both, - but it must start with an uppercase letter. - - - Looking at it with &ghci;, there's not much you can do with it yet: - - &data1.ghci:useless; - - Note that &ghci; doesn't know how to display Foo to - the screen, or how to compare it to itself. That's because we haven't - made our new type a member of the &Show; and &Eq; typeclasses. We'll - make a new example that is a member of these classes, and then we'll - get more information out of &ghci;. For more on typeclasses, refer to - . - - &data1.hs:silly2; - - We've also defined a function that takes any parameter and returns - a value of type Silly2. Let's play with this in - &ghci;. - - &data1.ghci:useless2; - - You can see here how the types interact. Since Foo - is of type Silly and Foo2 is of - type Silly2, you can't compare them directly. - - - Let's now expand on this - foundation with some more things that can be done with &data;. - - hunk ./en/ch13-data.xml 30 - - Multiple Type Constructors - - One way to make &data; more useful is to have multiple type - constructors. Here's an example: - - &data1.hs:color; - - This defined one new type named Color. You can - create a value of type Color by using any of the - three literals Red, Green, or - Blue. There are several reasons this might be - useful as opposed to using something like a &String; to store simple - color names: - - - You are guaranteed that a Color - always represents one of these three values - When you do pattern matching on a - Color, the compiler will warn you if you don't - consider all three possible values - It is possible to hide the implemention of - Color from users when exporting symbols from the - module; you can include Color in the list but - not the three constructors - - - Let's look at some example code that uses the Color: - - &data1.hs:color2; - - That's pretty simple: a function that takes a Color - and converts it to - a &String;. Note that we don't have to deal with the case where the - input to the function is something other than our three colors, because - that can't possibly happen. - - - You use a type defined in this way all the time. Haskell 98 defines - this: - - -data Bool = False | True deriving - (Read, Show, Eq, Ord, Enum, Bounded) - - - As you'll see in the rest of this chapter, many of the core Haskell - features you rely upon are actually defined in the prelude using - &data;. That is, they're not built into the compiler in any special - way. The only thing special about them is that they are loaded for you - by default since they're in the prelude. - - FIXME: have we discussed prelude? - + + Basic Type Creation + + To create a new type, we use the &data; keyword. In its most simple, + though probably uselessThe built-in type + () carries no data, so there is little need to + define your own unless you are writing FFI interfaces to C types. + , form, you can create a type like this: + + &data1.hs:useless; + + This defines a new type called Silly. There is one + type constructor for Silly: + Foo. When you write Foo in your + program, this Foo is a value of type + Silly. You can actually use the same word for both, + but it must start with an uppercase letter. + + + Looking at it with &ghci;, there's not much you can do with it yet: + + &data1.ghci:useless; + + Note that &ghci; doesn't know how to display Foo to + the screen, or how to compare it to itself. That's because we haven't + made our new type a member of the &Show; and &Eq; typeclasses. We'll + make a new example that is a member of these classes, and then we'll + get more information out of &ghci;. For more on typeclasses, refer to + . + + &data1.hs:silly2; + + We've also defined a function that takes any parameter and returns + a value of type Silly2. Let's play with this in + &ghci;. + + &data1.ghci:useless2; + + You can see here how the types interact. Since Foo + is of type Silly and Foo2 is of + type Silly2, you can't compare them directly. + + + Let's now expand on this + foundation with some more things that can be done with &data;. + + hunk ./en/ch13-data.xml 78 - - Defining Records - - Custom types need not be defined solely in terms of static data. They - can also take parameters. Let's say that we wanted to take our - color example a new direction and let a user represent arbitrary colors - in the RGB (red, green, blue) colorspace. We could use an - (Int, Int, Int) tuple for this, but to illustrate - records, let's define a new type. - - &color.hs:custom; - - This defines a new type CustomColor and one type - constructor also named CustomColor. This type - constructor, however, requires three parameters, all ∬s. - - - The type constructor here is a unique beast in Haskell. You can use it - as a function that takes three parameters, and it will return to you - one value ot type CustomColor. You can also use it - to perform pattern matching in function or case definitions. - - - Let's use &ghci; to inspect this for a bit: - - - You can see that CustomColor 100 0 50 returns a - single value of type CustomColor. Now you're - probably wondering how to extract the data from that - CustomColor. We use pattern matching to do that. - Here's how: - - &color.hs:extract; - - We used pattern matching to match the CustomColor. - The three ∬s were assigned to red, - green, and blue, and then printed - out. You can see that this worked by using &ghci;: - - &color.ghci:extract; - - This is useful as it is, but when you have half a dozen or more - values to store in your record, it can get annoying to have to match - them all. There's where named fields come in, which we'll discuss - next. - + + Multiple Type Constructors + + One way to make &data; more useful is to have multiple type + constructors. Here's an example: + + &data1.hs:color; + + This defined one new type named Color. You can + create a value of type Color by using any of the + three literals Red, Green, or + Blue. There are several reasons this might be + useful as opposed to using something like a &String; to store simple + color names: + + + You are guaranteed that a Color + always represents one of these three values + When you do pattern matching on a + Color, the compiler will warn you if you don't + consider all three possible values + It is possible to hide the implemention of + Color from users when exporting symbols from the + module; you can include Color in the list but + not the three constructors + FIXME: need to go into detail about exporting types + somewhere and add a ref to it from here + + + + Let's look at some example code that uses the Color: + + &data1.hs:color2; + + That's pretty simple: a function that takes a Color + and converts it to + a &String;. Note that we don't have to deal with the case where the + input to the function is something other than our three colors, because + that can't possibly happen. + + + You use a type defined in this way all the time. Haskell 98 defines + this: + + + data Bool = False | True deriving + (Read, Show, Eq, Ord, Enum, Bounded) + + + As you'll see in the rest of this chapter, many of the core Haskell + features you rely upon are actually defined in the prelude using + &data;. That is, they're not built into the compiler in any special + way. The only thing special about them is that they are loaded for you + by default since they're in the prelude. + + FIXME: have we discussed prelude? + hunk ./en/ch13-data.xml 136 + + Defining Records + + Custom types need not be defined solely in terms of static data. They + can also take parameters. Let's say that we wanted to take our + color example a new direction and let a user represent arbitrary colors + in the RGB (red, green, blue) colorspace. We could use an + (Int, Int, Int) tuple for this, but to illustrate + records, let's define a new type. + + &color.hs:custom; + + This defines a new type CustomColor and one type + constructor also named CustomColor. This type + constructor, however, requires three parameters, all ∬s. + + + The type constructor here is a unique beast in Haskell. You can use it + as a function that takes three parameters, and it will return to you + one value ot type CustomColor. You can also use it + to perform pattern matching in function or case definitions. + + + Let's use &ghci; to inspect this for a bit: + + + You can see that CustomColor 100 0 50 returns a + single value of type CustomColor. Now you're + probably wondering how to extract the data from that + CustomColor. We use pattern matching to do that. + Here's how: + + &color.hs:extract; + + We used pattern matching to match the CustomColor. + The three ∬s were assigned to red, + green, and blue, and then printed + out. You can see that this worked by using &ghci;: + + &color.ghci:extract; + + This sort of record is used in the standard Haskell library in the + System.Time.ClockTime type: + + + data ClockTime = TOD Integer Integer + deriving (Eq, Ord) + + + A ClockTime consists of two &Integer;s. The first + is the number of whole seconds since midnight UTC on January 1, 1970. + The second is an additional number of picoseconds. Since an &Integer; + can be negative and is unbounded, a ClockTime can + effectively represent any moment in history down to the picosecond. + + + This is useful as it is, but when you have half a dozen or more + values to store in your record, it can get annoying to have to match + them all. There's where named fields come in, which we'll discuss + next. + + }