Real World Haskell: Lecture 3 Bryan OSullivan 2009-10-21
Types: whats the big deal? If youve been following along and trying out homework, youre probably intimately familiar with the facts that Haskell is a statically typed language (it checks types before we run), and and its quite strict about this (its strongly typed).
What does strongly typed actually mean? Denition A strong type system guarantees that a program cannot contain certain kinds of errors. Strength and dynamism are more or less independent concepts in a type system. There are degrees of both strength and dynamism in type systems, and often disagreement on both, so the likelihood of confusion when talking to someone about types is high.
Familiar types Haskell provides a number of types similar to those youll have seen in other languages: Int is a xed-width signed integer type, usually the systems native word size (32 or 64 bits). Char is a Unicode character. Double is the double-precision oating point integer type, usually 64 bits. Integer is a signed integer type of unbounded size (unbounded if you have innite memory, that is). Bool is the Boolean type, with possible values True and False.
Expressions and types Every expression (and hence every value) in Haskell has a type. The expression a has the type Char. We can write this as follows: a : : Char a = a You can read the syntax :: as has the type, so: The notation a :: Char is called a type signature, and has the meaning the expression named a has the type Char.
Type inference Have you noticed that most of the homework answers submitted so far contain no type signatures? This is because the Haskell compiler infers what the type of every expression and denition is. When you write a = a, the compiler knows that the expression a can only have the type Char, so the variable a must have this type too.
Type inference and concision Unlike other statically typed languages you may be familiar with, hand-written type annotations are optional1 in Haskell. This is one of the big factors that lets us write amazingly concise code: We dont need to tell the compiler what our types are when it can gure this out for itself. 1 Well, theyre almost always optional.
Functions have types, too f a c t : : I n t e g e r > I n t e g e r fact n | n < 0 = error negative ! | o t h e r w i s e = go n where go 0 = 1 go i = i go ( i 1) A > symbol in a signature denotes the type of a function. On the left of the > is the type of the argument. The type on the right is that of the result. Read the arrow and its neighbours as this is a function from the type Integer to the type Integer.
Functions with multiple arguments Suppose we have a function with more than one argument. Heres how we write its type signature. c o n s : : Char > S t r i n g > S t r i n g cons x xs = x : xs Every item in the chain of arrows, except the last, is the type of the argument at that position. The last item in the chain is the type of the result. What is the type of x above? What is the type of xs? What is the functions result type?
Tuples An easy way to create a collection of values is using a tuple. b l a r g l e : : ( Char , I n t , Bool ) b l a r g l e = ( a , 42 , False ) The type signature (optional, as usual!) indicates what the type of each element in the tuple is. A tuple of two elements is usually called a pair. Tuples with three elements are often called triples. Larger tuples are 4-tuples, 5-tuples, and in general, n-tuples. (But 4-tuples and larger are very rare in the wild.) There is no one-tuple type (it would be redundant).
More about tuples It should be obvious that the following two tuple types can express dierent numbers of values: (Bool, Bool) (Bool, Bool, Char) As far as the type checker is concerned, they are indeed completely distinct types. This suggests that the following expressions might have dierent types: (True, a) (a, True) What do you think?
Functions on tuples Pairs are so common in Haskell that there are built-in functions, fst and snd for accessing the rst and second elements of a pair: fst (1 , a ) == 1 > snd ( 1 , a ) == a >
Pattern matching on tuples For larger tuples, built-in accessor functions are not supplied, since those types are used much less often. You can dene your own functions using pattern matching, and the syntax is as youd expect: fst3 (a , b , c) = a snd3 ( a , b , c ) = b thd3 ( a , b , c ) = c In practice, its more common to pattern-match than to dene and use accessor functions.
The list type The ghci interpreter has a very useful command, :type, for guring out what the type of an expression or function is. Heres an example: Prelude> :type lines lines :: String -> [String] As this suggests, the notation for list of String is [String]. And as the notation suggests, every element in a list must have the same type.
Type synonyms Recall that a Haskell string is just a list of characters. The type of a list of characters is [Char], but we often see String in type signatures. There is no magic at work here: String is just a synonym for [Char]. The compiler treats them identically. In fact, we can introduce our own type synonyms at will: type S t r e e t N u m b e r = I n t type StreetName = S t r i n g type A d d r e s s = ( StreetNumber , StreetName ) (Think typedef, C programmers!)
Functions over lists Type synonyms are only mildly interesting; I mentioned them mainly to bring the String [Char] equivalence to the fore. Heres why: notice that most functions on lists dont seem to care what types the elements of those lists have: drop 3 [ 0 . . 1 0 ] == [ 3 , 4 , 5 , 6 , 7 ] > drop 5 a b a c i n a t e == n a t e > Whats going on here?
Polymorphism Consider the following function denition: take n | n [ a ] > [ a ] That name a above, inside the list brackets, is a type variable. A type variable lets us say I dont know or care what concrete type2 will be used in this position, so heres a placeholder. 2 Concrete type? Think Int, Char, etc.
Multiple type variables We are not limited to a single type variable in a type signature. triple : : a > b > c > ( a , b , c ) triple x y z = (x , y , z) This means: The rst argument could have any type a. The second argument could have any type b. This type could be the same as, or dierent to, a. And the third? You get the idea.
Functions as parameters Suppose we want to inspect every element of a list, and drop those that do not suit some criterion. How should we write the type signature of a function that checks an element of the list? 3 Fancy language for function returning a Bool.
Functions as parameters Suppose we want to inspect every element of a list, and drop those that do not suit some criterion. How should we write the type signature of a function that checks an element of the list? Were checking some element of type a, and to indicate whether it passes or fails, we should return a Bool. So our type is: 3 Fancy language for function returning a Bool.
Functions as parameters Suppose we want to inspect every element of a list, and drop those that do not suit some criterion. How should we write the type signature of a function that checks an element of the list? Were checking some element of type a, and to indicate whether it passes or fails, we should return a Bool. So our type is: a > Bool How would we pass such a predicate3 as an argument to another function that will perform the ltering? 3 Fancy language for function returning a Bool.
The denition of lter Welcome to the world of higher-order programming! filter : : ( a > Bool ) > [ a ] > [ a ]