[Another ch03 snapshot. Bryan O'Sullivan **20070714235248] { addfile ./examples/ch03/Tree.hs addfile ./examples/ch03/tuple.ghci addfile ./examples/ch03/unit.ghci hunk ./en/ch03-funcs-types.xml 68 - Haskell's standard types + Some common basic types hunk ./en/ch03-funcs-types.xml 72 - built-in types that we'll use all the time. We can categorise - these into basic types and - compound types that we build from basic - types. + built-in types that we'll use over and over. Here are some of + the language's basic types, types that aren't composed of other + types hunk ./en/ch03-funcs-types.xml 76 - - Basic types + + + The Char type represents a character. The + values of Char are drawn from the Unicode + character set. + + + The Bool type represents a value in Boolean + logic. The possible values of Bool are + True and False. + + + The Int type represents a signed, + fixed-width integer. The exact range of values represented + by Int depends on the system's longest + native integer: on a 32-bit machine, an + Int is usually 32 bits wide, while on a 64-bit + machine, it is usually 64 bits wide. + + + The Integer type represents a signed + integer of arbitrary width. Integer values are + not used as often as Ints, because they're a + lot more expensive to work with. + + + The Double type is the type usually used to + represent floating point numbers. It is typically 64 bits + wide, and uses the machine's native floating point + representation. + + + hunk ./en/ch03-funcs-types.xml 110 - A basic type is simply one that isn't - constructed from any simpler types. + + Useful composite data types: lists and tuples hunk ./en/ch03-funcs-types.xml 113 - - - The Char type represents a character. The - values of Char are drawn from the Unicode - character set. - - - The Bool type represents a value in - Boolean logic. The possible values of Bool - are True and False. - - - The Int type represents a signed, - fixed-width integer. The exact range of values - represented by Int depends on the system's - longest native integer: on a 32-bit - machine, an Int is usually 32 bits wide, - while on a 64-bit machine, it is usually 64 bits - wide. - - - The Integer type represents a signed - integer of arbitrary width. Integer values - are not used as often as Ints, because - they're a lot more expensive to work with. - - - The Double type is the usual type used to - represent floating point numbers. It is typically 64 bits - wide. - - - + The most common composite data types in Haskell are the list + and tuple. + + We've already seen the list type mentioned in , where we found that Haskell + represents a text string as a list of Char values, + and that the type list of Char is + written [Char]. + + More generally, we can write the type list of + a for any type a by enclosing a in square brackets, [a]. + Lists are strongly typed: a list of one type has an identity of + its own, distinct from a list of another type. The type + [Int] is a list that can only contain values of + type Int, for example. + + A tuple is a fixed-size collection of values, each of which + can be of any type. Unlike a list, the elements of which must + all have the same type, there's no need for the elements of a + tuple to be related. + + &tuple.ghci:tuple; + + We can construct a tuple with any number of elements, + although in practice, they become unwieldy past a handful. Each + tuple encodes the types of its elements in its own type. + + &tuple.ghci:type; + hunk ./en/ch03-funcs-types.xml 187 - Remember the myValue variable we - defined? Here it is. + Remember the myValue variable we defined? Here + it is. hunk ./en/ch03-funcs-types.xml 213 + + + Algebraic data types hunk ./en/ch03-funcs-types.xml 217 - - Algebraic data types - - The Bool type that we introduced earlier is - the simplest example of a sort of type called an - algebraic data type. An algebraic data - type has a fixed set of possible values, each of which is - identified by a distinct constructor. + The Bool type that we introduced earlier is the + simplest example of a sort of type called an algebraic + data type. An algebraic data type has a fixed set + of possible values, each of which is identified by a distinct + constructor. hunk ./en/ch03-funcs-types.xml 223 - In the case of Bool, the type has two - constructors, True and False. Each - constructor is separated by a | character, - which we can read as or. These are usually - referred to as alternatives or cases. + In the case of Bool, the type has two + constructors, True and False. Each + constructor is separated by a | character, + which we can read as or. These are usually + referred to as alternatives or cases. hunk ./en/ch03-funcs-types.xml 231 - Each constructor can take zero or more arguments; the - numbers and types of the arguments accepted by each - constructor are independent. For example, here's one way we - might represent versions of the Windows operating system, - where old releases were monolithic, and newer releases have - service pack levels denoting major updates - after their initial releases. + Each constructor can take zero or more arguments; the + numbers and types of the arguments accepted by each constructor + are independent. For example, here's one way we might represent + versions of the Windows operating system, where old releases + were monolithic, and newer releases have service pack + levels denoting major updates after their initial + releases. hunk ./en/ch03-funcs-types.xml 241 - The alternatives that represent older releases don't need - arguments, but those for the newer releases need an - Int to represent the patch level. - - + The alternatives that represent older releases don't need + arguments, but those for the newer releases need an + Int to represent the patch level. hunk ./en/ch03-funcs-types.xml 289 - they're all using the same organising principle. + they're all using the same organising machinery. hunk ./en/ch03-funcs-types.xml 292 - for the name of an algebraic type and the name of one of its - constructors to be the same. It's always obvious from context - whether we're using a name as a type name or as a constructor, - so this doesn't introduce any ambiguity. + for the name of an algebraic type to have the same name as one + of its constructors to be the same. It's always obvious from + context whether we're using a name to refer to a type or a + constructor, so this doesn't introduce any ambiguity. hunk ./en/ch03-funcs-types.xml 303 - While it's also legal to give a constructor the same name - as its type when the type has multiple alternatives, this is - less common. + When a type has multiple constructors, it's still legal to + give one of the constructors the same name as the type, but + this is much less common. hunk ./en/ch03-funcs-types.xml 313 - Lists, parameterised types, and recursive types + Parameterised types hunk ./en/ch03-funcs-types.xml 315 - We've already seen the list type mentioned in , where we found that Haskell - represents a text string as a list of Char values, - and that the type list of Char is - written [Char]. + In our discussion of lists, we mentioned that we can create + a list of values of any type. We can define our own types that + allow this, too. To do this, we introduce variables into a type + declaration. hunk ./en/ch03-funcs-types.xml 320 - More generally, we can write the type list of - a for any type a by enclosing it in square brackets, - [a]. Lists are strongly typed: a list of one type - has a distinct identity from a list of another type. The type - [Int] is a list that can only contain values of - type Int, for example. + &Wrapper.hs:Wrapper; + + Here, the variable a is not a + regular variable; it's called a type + variable, because it indicates that our + Wrapper type takes another type as its parameter + (hence calling it a parameterised type). What this lets us do is + use Wrapper on values of any type. + + &Wrapper.hs:wrappedTypes; + + As usual, we can load our source file into &ghci; and + experiment with it. + + &wrapper.ghci:experiment; + + Wrapper is a generic container + type (albeit a fairly useless one); we can construct a + Wrapper from a value of any type. It is also + strongly typed; the type of whatever it contains is encoded in + its own type. + + To once again extend an analogy to more familiar languages, + this gives us a facility that bears some resemblance to + templates in C++, and to generics in Java. (In fact, Java's + generics facility was inspired by several aspects of Haskell's + type system.) + + We can nest uses of parameterised types inside each other, + but when we do, we may need to use parentheses to tell the + Haskell compiler what we mean. + + &Wrapper.hs:parens; + + + + Recursive types + + Here's a definition of a binary tree type. + + &Tree.hs:Tree; + + We call this a recursive type because + Tree, the type we're defining, appears both on the + left hand side and the right hand side of the definition: we + define the type in terms of itself. + + + + A little more about lists hunk ./en/ch03-funcs-types.xml 371 - A list is an algebraic data type, one that happens to be - built into the language. There are only two ways to construct a - list. One constructor is the empty list, written - []. + Now that we're getting familiar with some of the jargon + around types, we can revisit lists. Haskell's list type is a + parameterised type, because we can make lists of any other type. + It is also an algebraic data type, with two constructors. One + is the empty list, written [] (sometimes pronounced + nil, which is borrowed from Lisp). hunk ./en/ch03-funcs-types.xml 382 - construct, and borrowed from Lisp). The - : operator takes an element and a list, and - constructs a new list. - - &list.ghci:cons; + construct, and also borrowed from Lisp). The + (:) operator takes an element and a list, + and constructs a new list. hunk ./en/ch03-funcs-types.xml 386 - - Parameterised types + &list.ghci:cons; hunk ./en/ch03-funcs-types.xml 388 - In our discussion of lists, we mentioned that we can - create a list of any type. We can define our own types that - have this property. To do this, we introduce one or more - variables into a type declaration. + Because (:) constructs a list from + another list, the list type is recursive. So here we have a + built-in type that's parameterised, recursive, and + algebraic. hunk ./en/ch03-funcs-types.xml 393 - &Wrapper.hs:Wrapper; + One consequence of lists being generic is that lists of + lists, for example, aren't special in any way. hunk ./en/ch03-funcs-types.xml 396 - Here, the variable a is not - a regular variable; it's called a type - variable, because it indicates that our - Wrapper type takes another type as its parameter. - What this lets us do is use Wrapper on values of - any type. + &list.ghci:listlist; hunk ./en/ch03-funcs-types.xml 398 - &Wrapper.hs:wrappedTypes; + This has type [[String]], a list of lists of + strings. But since String is just a synonym for + [Char], it's really a list of + lists of lists of Char. Whew! + hunk ./en/ch03-funcs-types.xml 404 - As usual, we can load our source file into &ghci; and - experiment with it. + + Unit, the zero-element tuple hunk ./en/ch03-funcs-types.xml 407 - &wrapper.ghci:experiment; + Haskell has a special tuple type with no elements, written + (), and pronounced unit. hunk ./en/ch03-funcs-types.xml 410 - + &unit.ghci:unit; hunk ./en/ch03-funcs-types.xml 412 - - Recursive types + This type is only really used with parameterised data types, + to indicate that one of the type parameters isn't being used. + Since it doesn't encode any information, it's a rough equivalent + to void in C. hunk ./en/ch03-funcs-types.xml 417 - Because the definition of the list type refers to itself - (for example, the (:) operator constructs - a new list from an existing list), we refer to it as a - recursive data type. + Here's an example of () in use. We can + generalise our earlier Tree type a little, so that + internal nodes contain values of type a, while leaves contain values of type + b. hunk ./en/ch03-funcs-types.xml 423 - We can create recursive data types ourselves. - + &Tree.hs:ComplexTree; hunk ./en/ch03-funcs-types.xml 425 + If we wanted to create a ComplexTree where we + wanted to store Ints on the leaves, but don't care + about the internal nodes. We would write its type as + ComplexTree () Int. hunk ./examples/ch03/Tree.hs 1 +{-- snippet Tree --} +data Tree a = Node (Tree a) (Tree a) + | Leaf a + deriving (Show) + +{-- /snippet Tree --} + +{-- snippet ComplexTree --} +data ComplexTree a b = ComplexNode a (ComplexTree a b) (ComplexTree a b) + | ComplexLeaf b + deriving (Show) +{-- /snippet ComplexTree --} hunk ./examples/ch03/Wrapper.hs 14 +{-- snippet parens --} +multiplyWrapped :: Wrapper (Wrapper Int) + +multiplyWrapped = Wrapper (Wrapper 7) +{-- /snippet parens --} + hunk ./examples/ch03/list.ghci 9 +--# listlist + +[["foo", "bar"], ["x", "y"]] + hunk ./examples/ch03/tuple.ghci 1 +--# tuple +(True, "hello") +(4, ['a', 'm'], (16, True)) +--# type +:type (True, "hello") +:type (4 :: Int, ['a', 'm'], (16 :: Integer, True)) hunk ./examples/ch03/unit.ghci 1 +--# unit + +() +:type () hunk ./examples/ch03/wrapper.ghci 9 +:type Wrapper + }