Gentle Introduction to Haskell 98, Online Supplement
Part 2
Covers Section 2.1
Section: 2.1 Polymorphic Types
> module Part2() where
The following line allows us to redefine functions in the standard
prelude. Ignore this for now.
> import Prelude hiding (length,head,tail,null)
Start with some sample lists to use in test cases:
> list1 :: [Int]
> list1 = [1,2,3]
> list2 :: [Char] -- This is the really a String
> list2 = ['a','b','c'] -- This is the same as "abc"; evaluate list2 and see.
> list3 :: [[a]] -- The element type of the inner list is unknown
> list3 = [[],[],[],[]] -- so this list can't be printed
> list4 :: [Int]
> list4 = 1:2:3:4:[] -- Exactly the same as [1,2,3,4]; print it and see.
This is the length function. You can test it by evaluating expressions
such as `length list1'. Function application is written by
simple juxtaposition: `f(x)' in other languages would be `f x' in Haskell.
> length :: [a] -> Int
> length [] = 0
> length (x:xs) = 1 + length xs
Function application has the highest precedence, so 1 + length xs is
parsed as 1 + (length xs). In general, you have to surround
non-atomic arguments to a function with parens. This includes
arguments which are also function applications. For example,
f g x is the function f applied to arguments g and x, similar to
f(g,x) in other languages. However, f (g x) is f applied to (g x), or
f(g(x)), which means something quite different! Be especially
careful with infix operators: f x+1 y-2 would be parsed as (f x)+(1 y)-2.
This is also true on the left of the `=': the parens around (x:xs) are
absolutely necessary. length x:xs would be parsed as (length x):xs.
Also be careful with prefix negation, -. The application `f -1' is
f-1, not f(-1). Add parens around negative numbers to avoid this
problem.
Here are some other list functions:
> head :: [a] -> a -- returns the first element in a list (same as car in lisp)
> head (x:xs) = x
> tail :: [a] -> [a] -- removes the first element from a list (same as cdr)
> tail (x:xs) = xs
> null :: [a] -> Bool
> null [] = True
> null (x:xs) = False
> cons :: a -> [a] -> [a]
> cons x xs = x:xs
> nil :: [a]
> nil = []
Length could be defined using these functions. This is not good
Haskell style but does illustrate these other list functions.
Haskell programmers feel that the pattern matching style, as used in
the previous version of length, is more natural and readable.
> length' :: [a] -> Int -- Note that ' can be part of a name
> length' x = if null x then 0 else 1 + length' (tail x)
A test case for length', cons, and nil
> e1 = length' (cons 1 (cons 2 nil))
We haven't said anything about errors yet. Each of the following
examples illustrates a potential runtime or compile time error. The
compile time error is commented out so that other examples will compile;
you can uncomment e2 and see what happens.
> -- e2 = cons True False -- Why is this not possible in Haskell?
> e3 = tail (tail ['a']) -- What happens if you evaluate this?
> e4 = [] -- This is especially mysterious!
This last example, e4, is something hard to explain but is often
encountered early by novices. We haven't explained yet how the system
prints out the expressions you type in - this will wait until later.
However, the problem here is that e4 has the type [a]. The printer for
the list datatype is complaining that it needs to know a specific type
for the list elements even though the list has no elements! This can
be avoided by giving e4 a type such as [Int]. (To further confuse you,
try giving e4 the type [Char] and see what happens.)
Continued in part3.lhs