A Haskell program is a collection of modules, one of which, by convention, must be called Main and must export the value main. The value of the program is the value of the identifier main in module Main, and main must have type Dialogue.
Modules may reference other modules via explicit import declarations, each giving the name of a module to be imported, specifying its entities to be imported, and optionally renaming some or all of them. Modules may be mutully recursive.
The name space for modules is flat, with each module being associated with a unique module name.
There are no mandatory type declarations, although Haskell programs often contain type declarations. The language is strongly typed. No delimiters (such as semicolons) are required at the end of definitions - the parsing algorithm makes intelligent use of layout. Note that the notation for function application is simply juxtaposition, as in sq n.
Single line comments are preceded by ``--'' and continue to the end of the line. For example:
Multiline and nested comments begin with {- and end with -}. Thussucc n = n + 1 -- this is a successor function
{- this is a multiline comment -}
Haskell provides two different methods for enclosing declaration lists. Declarations may be explicitly enclosed between braces { } or by the layout of the code.
For example, instead of writing:
one may write:f a + f b where { a = 5; b = 4; f x = x + 1 }
Function application is curried, associates to the left, and always has higher precedence than infix operators. Thus ``f x y + g a b'' parses as ``((f x) y) + ((g a) b)''f a + f b = where a = 5; b = 4 f x = x + 1
Expressions are syntactic terms that denote values and thus have an associated type.
Types may be polymorphic --- i.e. they may contain type variables which are universally quantified over all types. Furthermore, it is always possible to statically infer this type. User supplied type declarations are optional
If ei has type ti then the tuple has type (t1, t2, ..., tn)(e1, e2, ..., en) n >= 2
Lists have the form: [e1, e2, ..., en] where n >= 0 and every element ei must have the same type, say t, and the type of the list is then [t]. The above list is equivalent to: e1:e2:...:en:[] that is, ``:'' is the infix operator for ``cons''.
data T u1 ... un = C1 t11 ... t1k1where T is a type constructor; the ui are type variables; the Ci are (data) constructors; and the tij are the constituent types (possibly containing some ui).
| ...
| Cn tn1 ... tnkn
The presence of the ui implies that the type is polymorphic --- it may be instantiated by substituting specific types for the ui.
Here are some examples:
Bool and Color are nullary type constructors because they have no arguments. True, False, Red, etc are nullary data constructors. Bool and Color are enumerations because all of their data constructors are nullary. Point is a product or tuple type constructor because it has only one constructor; Tree is a union types; often called an algebraic data type.data Bool = True | False data Color = Red | Green | Blue | Indigo | Violet data Point a = Pt a a data Tree a = Branch (Tree a) (Tree a) | Leaf a
is a lambda abstraction and is equivalent to the function succ defined by:\x -> x+1
If ``x'' has type t1 and ``exp'' has type t2 then `` \x -> exp'' has type t1->t2. Function definitions and lambda abstractions are ``curried'', thus facilitating the use of higher-order functions. For example, given the definitionsucc x = x + 1
the function succ defined earlier might be redefined as:add x y = x + y
The curried form is useful in conjunction with the function map which applies a function to each member of a list. In this case,succ = add 1
map applies the curried function add 1 to each member of the list [1,2,3] and returns the list [2,3,4].map (add 1) [1, 2, 3] => [2,3,4]
Functions are defined by using one or more equations. To illustrate the variety of forms that function definitions can take are are several definitions of the factorial function. The first definition is based on the traditional recursive definition.
The second definition uses two equations and pattern matching of the arguments to define the factorial function.fac n = if n == 0 then 1 else n*fac( n - 1)
The next definition uses two equations, pattern matching of the arguments and uses the library function product which returns the product of the elements of a list. It is more efficient then the traditional recursive factorial function.fac 0 = 1 fac (n+1) = (n+1)*fac(n)
The final definition uses a more sophisticated pattern matching scheme and provides error handling.fac 0 = 1 fac (n+1) = product [1..(n+1)]
The infix operators are really just functions. For example, the list concatenation operator is defined in the Prelude as:fac n | n < 0 = error "input to fac is negative" | n == 0 = 1 | n > 0 = product [1..n]
Since infix operators are just functions, they may be curried. Curried operators are called sections. For example, the first two functions add three and the third is used when passing the addition function as a parameter.(++) :: [a] -> [a] -> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs++ys)
(3+) (+3) (+)
The first equation uses the builtin error function, which causes program termination and printing of the string as a diagnostic.quadsolve a b c | delta < 0 = error "complex roots" | delta == 0 = [-b/(2*a)] | delta > 0 = [-b/(2*a) + radix/(2*a), -b/(2*a) - radix/(2*a)] where delta = b*b - 4*a*c radix = sqrt delta
Where clauses may occur nested, to arbitrary depth, allowing Haskell programs to be organized with a nested block structure. Indentation of inner blocks is compulsory, as layout information is used by the parser.
``Tree Int'' is type of trees of fixnums; ``Tree (Char -> Bool)'' is the type of trees of functions mapping characters to Booleans, etc. Furthermore:data Tree a = Branch (Tree a) (Tree a) | Leaf a
``fringe'' has type ``Tree a -> [a]'', i.e. ``for all types a, fringe maps trees of a into lists of a.fringe (Leaf x) = [x] fringe (Branch left right) = finge left ++ fringe right
Here
id has type a->a, (++) (append) has type: [a]->[a]->[a], and map has type (a->b)->[a]->[b]. These types are inferred automatically, but may optionally be supplied as type signatures:id x = x [] ++ ys = ys (x:xs) ++ ys = x : (xs++ys) map f [] = [] map f (x:xs) = f x : map f xs
id :: a -> a (++) :: [a] -> [a] -> [a] map :: (a->b) -> [a] -> [b]
This definition of String is part of Haskell, and in fact the literal syntax "hello" is shorthand for:type String = [Char] type Person = (Name, Address) type Name = String data Address = None | Addr String
['h','e','l','l','o']
Functions may be defined by giving several alternative equations, provided the formal parameters have different patterns. This provides another method of doing case analysis which is often more elegant than the use of guards. We here give some simple examples of pattern matching on natural numbers, lists, and tuples. Here is (another) definition of the factorial function, and a definition of Ackerman's function:
Accessing the elements of a tuple is also done by pattern matching. For example the selection functions on 2-tuples can be defined thus
Here are some simple examples of functions defined by pattern matching on lists:fst (a,b) = a snd (a,b) = b
n+k -- patterns are useful when writing inductive definitions over integers. For example:sum [] = 0 sum (a:x) = a + sum x product [] = 0 product (a:x) = a * product x reverse [] = [] reverse (a:x) = reverse x ++ [a]
As-patterns are used to name a pattern for use on the right-hand side. For example, the function which duplicates the first element in a list might be written as:x ^ 0 = 1 x ^ (n+1) = x*(x^n) fac 0 = 1 fac (n+1) = (n+1)*fac n ack 0 n = n+1 ack (m+1) 0 = ack m 1 ack (m+1) (n+1) = ack m(ack (m+1) n)
but using an as-pattern as follows:f (x:xs) = x:x:xs
Wild-cards. A wild-card will match anything and is used where we don't care what a certain part of the input is. For example:f s@(x:xs) = x:s
head (x:_) = x tail (_:xs) = xs
is semantically equivalent to:f p11 ... p1k = e1 ... f pn1 ... pnk = en
f x1 ... xk = case (x1, ..., xk) of (p11, ..., p1k) -> e1 ... (pn1, ..., pnk) -> en
Note that lists are subscripted beginning with 0. The following table summarizes the list operators.["Mon","Tue","Wed","Thur","Fri"] ++ ["Sat","Sun"] is ["Mon","Tue","Wed","Thur","Fri","Sat","Sun"] [1,2,3,4,5] [2,4] is [1,3,5] 0:[1,2,3] is [0,1,2,3] [0,1,2,3]!!2 is 2
Symbol | Operation |
x:List | prefix an element to a list |
List ++ List | concatenate two lists |
List \\ List | list difference |
List !! n | n-th element of a list n = 0.. |
In the second list, the difference between the first two elements is used to compute the remaining elements in the series.[1..5] -- yields [1,2,3,4,5] [1,3..10] -- yields [1,3,5,7,9]
This is a list containing (in order) the squares of all the numbers from 1 to 100. The above expression would be read aloud as ``list of all n*n such that n is drawn from the list 1 to 100''. Note that ``n'' is a local variable of the above expression. The variable-binding construct to the right of the bar is called a ``generator'' - the ``<-'' sign denotes that the variable introduced on its left ranges over all the elements of the list on its right. The general form of a list comprehension in Haskell is:[ n*n | n <- [1..100] ]
where each qualifier is either a generator, of the form: var <- exp, or else a filter, which is a boolean expression used to restrict the ranges of the variables introduced by the generators. When two or more qualifiers are present they are separated by commas. An example of a list comprehension with two generators is given by the following definition of a function for returning a list of all the permutations of a given list,[ body | qualifiers ]
The use of a filter is shown by the following definition of a function which takes a number and returns a list of all its factors,perms [] = [[]] perms x = [ a:y | a <- x; y <- perms (x [a]) ]
List comprehensions often allow remarkable conciseness of expression. We give two examples. Here is a Haskell statement of Hoare's ``Quicksort'' algorithm, as a method of sorting a list,factors n = [ i | i <- [1..n]; n `mod` i = 0 ]
Here is a Haskell solution to the eight queens problem. We have to place eight queens on chess board so that no queen gives check to any other. Since any solution must have exactly one queen in each column, a suitable representation for a board is a list of integers giving the row number of the queen in each successive column. In the following program the function "queens n" returns all safe ways to place queens on the first n columns. A list of all solutions to the eight queens problem is therefore obtained by printing the value of (queens 8)quicksort :: [a] -> [a] quicksort [] = [] quicksort (p:xs) = quicksort [ x | x <- xs, x <= p ] ++ [ p ] ++ quicksort [ x | x <- xs, x > p ]
queens 0 = [[]] queens (n+1) = [ q:b | b <- queens n; q <- [0..7]; safe q b ] safe q b = and [ not checks q b i | i <- [0..(b-1)] ] checks q b i = q=b!!i || abs(q - b!!i)=i+1
and then use it in such situations as ``cond (x=0) 0 (1/x)''.cond True x y = x cond False x y = y
The other main consequence of lazy evaluation is that it makes it possible to write down definitions of infinite data structures. Here are some examples of Haskell definitions of infinite lists (note that there is a modified form of the ``..'' notation for endless arithmetic progressions)
The elements of an infinite list are computed ``on demand'', thus relieving the programmer of specifying ``consumer-producer'' control flow.nats = [0..] odds = [1,3..] ones = 1 : ones nums_from n = n : nums_from (n+1) squares = [ x**2 | x <- nums_from 0 ] odd_squares xs = [ x**2 | x <- xs, odd x ] cp xs ys = [ ( x, y ) | x <- xs, y <- ys ] -- Cartesian Product pyth n = [ ( a, b, c ) | a <- [1..n], -- Pythagorean Triples b <- [1..n], c <- [1..n], a + b + c <= n, a^2 + b^2 == c^2 ] squares = [ n*n | n <- [0..] ] fib = 1:1:[ a+b | (a,b) <- zip fib ( tail fib ) ] primes = sieve [ 2.. ] where sieve (p:x) = p : sieve [ n | n <- x, n `mod` p > 0 ] repeat a = x where x = a : x perfects = [ n | n <- [1..]; sum(factors n) = n ] primes = sieve [ 2.. ] where sieve (p:x) = p : sieve [ n | n <- x; n mod p > 0 ]
One interesting application of infinite lists is to act as lookup tables for caching the values of a function. For example here is a (naive) definition of a function for computing the n'th Fibonacci number:
This naive definition of ``fib'' can be improved from exponential to linear complexity by changing the recursion to use a lookup table, thusfib 0 = 0 fib 1 = 1 fib (n+2) = fib (n+1) + fib n
alternatively,fib 0 = 1 fib 1 = 1 fib (n+2) = flist!!(n+1) + flist!!n where flist = map fib [ 0.. ]
Another important use of infinite lists is that they enable us to write functional programs representing networks of communicating processes. Consider for example the Hamming numbers problem - we have to print in ascending order all numbers of the form 2^a*3^b*5^c, for a,b,c>=0. There is a nice solution to this problem in terms of communicating processes, which can be expressed in Haskell as followsfib n = fiblist !! n where fiblist = 1:1:[a+b| (a,b) <- zip fiblist (tail fiblist) ]
hamming = 1 : merge (f 2) (merge (f 3) (f 5)) where f a = [ n*a | n <- hamming ] merge (a:x) (b:y) = a : merge x (b:y), if a<b = b : merge (a:x) y, if a>b = a : merge x y, otherwise
The constructors Empty and Stk, which comprise ``the implementation'' are not exported, and thus hidden outside of the module. To make the datatype concrete, one would write:module Stack (StackType, push, pop, top, empty) where data StackType a = Empty | Stk a (StackType a) push x s = Stk x s pop (Stk _ s) = s top (Stk x _) = x empty = Empty
module Stack (StackType(Empty,Stk), push, ...) ...
In Haskell every function of two or more arguments is actually a higher order function. This permits partial parameterization. For example member is a library function such that member x a tests if the list x contains the element a (returning True or False as appropriate). By partially parameterizing member we can derive many useful predicates, such as
As another example of higher order programming consider the function foldr, defined byvowel = member ['a','e','i','o','u'] digit = member ['0','1','2','3','4','5','6','7','8','9'] month = member ["Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"]
All the standard list processing functions can be obtained by partially parameterizing foldr. Here are some examples.foldr op k [] = k foldr op k (a:x) = op a (foldr op k x)
sum = foldr (+) 0 product = foldr (*) 1 reverse = foldr postfix [] where postfix a x = x ++ [a]
Types | Values |
Bool | True, False |
Char | the ASCII character set |
Int | minInt, ..., maxInt |
Integer | arbitrary precision integers |
Float | floating point, single precision |
Double | floating point, double precision |
Bin | binary numbers |
String | list of characters |
Funtions | lambda abstractions and definitions |
Lists | lists of objects of type T |
Tuples | Algebraic data types |
Numbers | integers and floating point numbers |
Type | Representation | Values |
list | [ comma separated list ] | user defined |
tuple | ( comma separated list ) | user defined |
Here are several examples of lists and a tuple:
[] ["Mon","Tue","Wed","Thur","Fri"] [1,2,3,4,5] ("Jones",True,False ,39)
e :: twhere e is an expression and t is a type. For example, the factorial function has type
fac :: Integer -> Integerwhile the function length which returns the length of a list has type
length :: [a] -> Integerwhere [a] denotes a list whose elements may be any type.
Predicate | Checks if |
digit | argument is a digit |
letter | argument is a letter |
integer | argument is an integer |
Symbol | Operation |
+ | addition |
- | subtraction |
* | multiplication |
/ | real division |
div | integer division |
mod | modulus |
^ | to the power of |
Symbol | Operation |
not | negation |
&& | logical conjunction |
|| | logical disjunction |
Symbol | Operation |
== | equal |
/= | not equal |
< | less than |
<= | less than or equal |
> | greater than |
>= | greater than or equal |
module Tree ( Tree(Leaf,Branch), fringe ) where data Tree a = Leaf a | Branch ( Tree a ) ( Tree a ) fringe :: Tree a -> [a] fringe ( Leaf x ) = [x] fringe ( Branch left right ) = fringe left ++ fringe right
y
Send comments to: webmaster@cs.wwc.edu