Haskell Book
Haskell Book
Haskell Book
Mihai-Radu Popescu
questions@sthaskell.com
To
#haskell,
Contents
I.
1.
Starting Out
Introduction
1
2
1.1. 1.2.
About the Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Why Haskell? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1. 1.2.2. 1.2.3. For Programmers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . For Mathematicians For Everybody Else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 2 2 3 3 4 4 4 5
6
1.3.
Before We Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1. 1.3.2. 1.3.3. Using GHCi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Interactive vs. Noninteractive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Loading Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.
2.1.
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1. 2.1.2. 2.1.3. 2.1.4. Simple Arithmetic Boolean Algebra Inx Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6 6 6 7 8 9 9 10 12 13 14 14 15 16
17
2.2.
2.3.
Practical Applications
3.
3.1.
Understanding Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.1. 3.1.2. Knowing Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Type Declarations Type Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17 17 18 19 19 19 20 21 22 22 23 24 25
3.2.
Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1. 3.2.2. 3.2.3. 3.2.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Typeclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Making Polymorphic Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Drawbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.
Case Study: Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3.1. 3.3.2. 3.3.3. 3.3.4. Lists Recap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Understanding Tuples Functions on Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
iii
Contents
II.
4.
27
28
4.1.
Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.1. 4.1.2. 4.1.3. 4.1.4. 4.1.5. Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Matching with Cons As patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28 28 30 31 32 33 34 34 36 39 41
43
4.2.
5.
Recursion
5.1.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43 43 45 46 47 47 48 49 49 49 50 51
53
Understanding Recursion
Practical Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . More Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Using Guards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Multiple Regular Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Innite Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Using Natural Numbers [FIXME-move to adv. types] . . . . . . . . . . . . . . . . . . . Application: Quicksort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.
5.3.
6.
Advanced Functions
6.1.
Currying and Partial Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.1.1. 6.1.2. 6.1.3. Fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Problem Z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . When It's Not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53 53 54 55 56 56 57 58 58 60 62 63 65 65 67
6.2.
6.3.
map
and
zipWith
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Working with Predicates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comparison with List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . . . . . Anonymous Functions (Lambdas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eating a List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.
III. Appendices
A. Miscellaneous
68
69
A.1. Functions
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69 69 70
A.1.1. Fixity
iv
Contents
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71 71
74
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74 74 75 76 78 78 79 80
82
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
B.1.3. Numeric Typeclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2. Type Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2.1. General Type Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2.2. Ambiguous Type Variable Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B.2.3. Making Custom Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
C. Modules
C.1. Data.List
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
82
Part I.
Starting Out
1. Introduction
The Haskell community has an acute shortage of buggy underdocumented programs.
(sorear)
already familiar (or too familiar) with programming in another language, you might need to put in extra work. Don't be discouraged! While the stu in the beginning may seem extremely boring, mind-blowing things start happening later on. This book has a lot of footnotes. You don't have to read them, but sometimes you might gain some insight by doing so. You can click on them (do it here ) to jump to them faster (readers from the website might want to download the book for this reason). You can click on the table of contents as well. The writing in this book may not be polished yet, and some things may be missing, but take a look you might just like it!
Extensive programming experience A background in mathematics An inclination towards the abstract Perseverence Hard work
If it didn't work, you might want to download the book (google docs link). If it still doesn't work, get Adobe Reader.
1. Introduction
return
doesn't return
Classes aren't really classes Variables are actually constants. The code
might not
Below are some of my favorite snippets of code, each on a separate line. They're classics, and really show how Haskell stands out. 1 2 3 4 5
fibonacci = 0:1: zipWith (+) fibonacci ( tail fibonacci ) primes = nubBy (\ x y -> ( gcd x y ) > 1) [2..] rationals = fix ((1:) . ( > >= \x -> [x +1 , 1%( x +1) ]) ) :: [ Rational ] powerset = filterM ( const [ True , False ]) histogram = map ( head &&& length ) . group . sort
i = i + 1,
cringed at the sight of a computer screen with some random code. They are used to writing stu like: Let a function f : Z Z, f (x) = 2y + 3, where y = |x 4|. If we consider set A = {5, 3, . . . , 11}, we shall map function f over A, naming the result set B . We shall also 2 consider set C = f (x) |x A, x < 10 . One does not simply code such a thing in C or Python at least not without mutilating maths. However, in Haskell, the result is pleasing to the eye and easy to understand, too (everything following the comment). 1 2 3 4 5 6 7
is a
f :: Integer -> Integer f x = 2* y + 3 where y = abs (x -4) a = [ -5 , -3..11] -- we ' ll see later why a , b , and c are lowercase b = map f a c = [( f x) ^2 | x <- a , x < 10] -- this really works !
The mathematical applications of Haskell are endless. It's even possible to dene and work with monoids [XREF]!
Problem Z
even though it's actually a feature), and more, will be presented and
1. Introduction
ee@bt :~ $ ghci GHCi , version 7.4.1: http :// www . haskell . org / ghc / Loading package base ... linking ... done . Prelude > 2 + 3 5 Prelude > max 10 2 10
:? for help
Prelude > :l test . hs -- loading a file [1 of 1] Compiling Main ( test . hs , interpreted ) Ok , modules loaded : Main . * Main > import Control . Monad -- importing a module * Main Control . Monad > : set prompt " ghci > " ghci > :q -- you can also exit with Ctrl -D Leaving GHCi . ee@bt :~ $
ghci>
1 2
inside
1. Introduction
-- File : basic . hs a = 2 b = 3 c = a + b
Now let's load this into GHCi and see if it works (the le needs to be in the directory where GHCi was started, or it won't work ).
1 2 3 4 5 6 7 8 9 10 11
ghci > :l basic . hs -- this is how we load [1 of 1] Compiling Main ( basic .hs , interpreted ) Ok , modules loaded : Main . ghci > a + 1 3 ghci > c - b == a True ghci > :r -- this reloads the file if we change it [1 of 1] Compiling Main ( basic .hs , interpreted ) Ok , modules loaded : Main . ghci >
Again, there is no need to dissect the above pieces of code what's important is knowing how to load a le (:l
file.hs)
Actually, if the full path is given it will work just ne, but it's cumbersome.
(kzm)
abs, exp
ghci > 4 + 5*6 34 ghci > exp 2 7.38905609893065 ghci > 10 - 4 - ( max 5 6) 0 ghci > 10^60 1000000000000000000000000000000000000000000000000000000000000
There still are some problems, especially with the
operator.
1 2 3 4 5
ghci > -3 -3 ghci > -3 + 4 1 ghci > min -3 4 -- this gives a very long error message .
GHCi treats
min -3 4
as
min - (3 4),
3 4
from
min.
This may
look strange, even downright stupid, but GHCi has a very good reason: arguments is essential in Haskell. We have no choice but to oblige a solution is to wrap 1 2
-3
in parentheses.
ghci > False || False -- right associative False ghci > True || False && False -- && has a higher precedence True
5 6 7 8 9 10 11 12
ghci > False ghci > True ghci > False ghci > True
not True not False || not True 5 == 6 -- equality is not associative 5 /= 7 -- programmers beware , it ' s not !=
True
laziness
&&
False.
||
||
and
&&
are not built into the language, they're functions like all others.
ghci > 4 ghci > 'b ' ghci > 'X ' ghci >
succ 3 -- needs to have a logical successor succ 'a ' pred 'Y ' -- same here pred " Hello " -- error
There is an important distinction to be made regarding function calls. Parentheses around the arguments only set precedence, not separate the function from the arguments. It's essential not to get fooled, especially in the next example. 1 2 3 4 5 6
ghci > foo ( bar 10) -- in C this would be foo ( bar (10) ) ghci > ( foo bar ) 10 ghci > foo bar 10 -- this is equivalent to the above ghci > foo bar ( baz 10) 8 -- in C: foo ( bar , baz (10) , 8)
Also, function application has the highest precedence, so if you write (for more details see A.1.1). We're slightly familiar with dening functions, too (the 1.2.2 example). Let's play a little more with them. Obviously, we can refer to other functions in a denition. Another thing to note is that functions can't begin with uppercase letters.
foo 10 + 8,
it means
(foo 10) + 8
1 2 3 4 5 6
-- File : functions . hs triple x = 3* x strangeAddition x y = x + triple y squareTwo x y = (x + y) ^2 c = 4 -- this one takes zero parameters
Technically all functions accept only one parameter, but it's not healthy to think like this, at least for now remember
Problem Z
(introduced in 1.2.3)?
Before we start... calling around, let's talk a little about the last line. This is a very interesting case indeed
is what we would call in other languages a variable. It's declared the same as a function, but it takes
c = 4
then
c = 5
Unlike most languages, in Haskell a zero-parameter function and a constant are really the same. strangely enough, has something to do with 1 2 3 4 5 6 7 8 9 10 11 12 13
Problem Z
ghci > :l functions . hs [1 of 1] Compiling Main Ok , modules loaded : Main . ghci > triple 2 6 ghci > strangeAddition 10 20 70 ghci > squareTwo 5 6 121 ghci > triple c 12 ghci > strangeAddition ( triple 2) c 18
Before we continue, let's look a bit at Haskell's if-else. The rst thing we notice is that the no variables to change, so a function that doesn't return anything wouldn't work . Does f sense? Let's add something to
else
part is make
mandatory. Why? Every function has to return something. Why? Haskell is more like maths there are
(x) =
functions.hs
happens. Indentation is essential in Haskell because that's how the interpreter identies blocks of code. 1 2 3 4
-- File : functions . hs ( CONTINUED ) strangeAddition ' x y = if x > y then x + triple y else y + triple x ghci > :r -- we won 't be showing load / reload from now on [1 of 1] Compiling Main ( functions .hs , interpreted ) Ok , modules loaded : Main . ghci > strangeAddition 5 3 14 ghci > strangeAddition 3 5 18 ghci > strangeAddition ' 5 3 14 ghci > strangeAddition ' 3 5 14
1 2 3 4 5 6 7 8 9 10 11
functions with backquotes, we can make them inx (put them between the parameters), much like
or
*.
Mathematicians will understand this right away. There is also a technical reason, explained in detail in [XREF]
1 2 3 4 5
ghci > 3 ` squareTwo ` 4 49 ghci > 10 ` strangeAddition ` 20 70 ghci > 2 ` triple ` -- error ( and looks stupid , too )
Backquotes are usually adopted to make functions more readable, but they can also be used to create chains. Watch out for associativity (default left) and precedence (default highest) built-in functions don't use the defaults (see A.1.1).
1 2 3 4 5 6
ghci > 2 ` squareTwo ` 3 ` squareTwo ` 4 ` squareTwo ` 5 715716 ghci > ((2 ` squareTwo ` 3) ` squareTwo ` 4) ` squareTwo ` 5 715716 ghci > 2 ` squareTwo ` (3 ` squareTwo ` (4 ` squareTwo ` 5) ) 49815364
If a function name contains only symbols (like
++, ^,
or
-.-),
functions before the arguments, by putting them in parentheses. This really helps with 1 2 3 4 5 6
Problem Z.
ghci > (+) 2 3 5 ghci > (*) 4 5 20 ghci > (/) 10 4 2.5
1 2 3 4 5 6
Are homogenous mixing, for example, numbers with characters gives an error. Have variable length . Can be innitely long . Are singly linked lists can only be traversed from left to right .
We'll dene some lists in a le so we can explore functions that operate on them.
-- File : lists . hs numbers = [1 , 3, 7, 5, 6 , 6, 8, 10] languages = [" lisp " , " haskell " , "c " , " perl " , " ruby " , " python " ] hello = " Hello , World !" -- same as [ 'H ' , 'e ', 'l ' , 'l ', ... and so on ] listOfLists = [[1 , 5 , 7, 9] , [2 , 4, 6] , [1]] emptyList = []
For starters,
is equivalent to
(a ++ b) ++ c
4 5 6 7
Well, technically speaking they can't change (nothing can), but for all intents and purposes they are variable in length. This is because of Functions in Haskell (like those from 2.1.2) are made to use only as much information as is necessary, and not more. If we combine with
&&
an innite number of
Falses,
This means that accessing the last element requires going through the whole list watch out! Without this basic property, lists would be stupid.
1 2 3 4
ghci > [1 , 2, 3] ++ [5 , 4] [1 ,2 ,3 ,5 ,4] ghci > " Haskell " ++ " " ++ " is " ++ " " ++ " fun " " Haskell is fun "
The simplest list operator is
[1, 2, 3]
1 2 3 4 5 6 7 8
1:2:3:[].
:,
but
ghci > 5 : [4 , 6 , 8] [5 ,4 ,6 ,8] ghci > 5 : 4 : 6 : 8 : [] [5 ,4 ,6 ,8] ghci > 'f ' : " iretruck " " firetruck " ghci > [3 , 4] : [[5 , 6, 7] , [8 , 9]] [[3 ,4] ,[5 ,6 ,7] ,[8 ,9]]
The following throw errors because we're not using however.
correctly.
1 2 3
ghci > [1] : [2 , 3] -- use 1 : [2 , 3] or [1] ++ [2 , 3] instead . ghci > 1 : 2 : 3 -- use 1 : 2 : [3] or 1 : 2 : 3 : [] ghci > [10 , 9, 2] : 4 -- use [10 , 9 , 2] ++ [4]
xs10 ):
rst element all but the rst last element all but the last the n
not
to do it:
8 9 10
is called a list constructor (or cons for short). It's the operator that links the elements of a list (we'll see how this happens
a bit later, in [XREF]) The same thing, but prettier. As in the plural form of
etc.
10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
ghci > let xs = [1 , 2, 3, 4 , 5, 6] ghci > head xs 1 ghci > tail xs [2 ,3 ,4 ,5 ,6] ghci > last xs 6 ghci > init xs [1 ,2 ,3 ,4 ,5] ghci > xs !! 4 5 ghci > take 2 xs [1 ,2] ghci > drop 2 xs [3 ,4 ,5 ,6] ghci > length xs 6 ghci > null xs False
One thing worth pointing out is that, due to the nature of lists in Haskell, accessing the last element of a list is considerably slower than accessing the rst one. This is because, internally, accessing an element requires going through
and
!!
throws an exception.
ghci > head [] *** Exception : Prelude . head : empty list ghci > l !! 100 *** Exception : Prelude .(!!) : index too large ghci > l !! ( -2) *** Exception : Prelude .(!!) : negative index
Some more useful functions:
12
elem
This is not entirely accurate, but it will do for now. To calculate the maximum, the elements need to have some sort of logical order. A list of numbers or a list of characters are ne, but a list of functions is not. Needs to be able to equate elements. This may seem pretty standard, but not all stu can equal other stu (we'll discuss this in-depth in [XREF]).
11
7 8 9 10 11 12 13 14 15
32 ghci > 9600 ghci > True ghci > False ghci > True
A special case, 1 2 3 4 5 6
concat,
operates on lists of lists: it attens them. It only removes one layer, though.
ghci > concat [[2 ,3] ,[4 ,5]] [2 ,3 ,4 ,5] ghci > concat [[5]] [5] ghci > concat [[[5]]] [[5]]
There are some functions that operate on lists of
and or
1 2 3 4 5 6 7 8
returns
True
True
if at least one is
True, False
otherwise.
ghci > False ghci > True ghci > True ghci > False
and [ True , True , False ] and [ True , True , True ] or [ True , False , False ] or [ False , False , False ]
And neither last nor least (see C.1 for more), reversing long lists. 1 2
2.2.3. Ranges
Many times we need to construct lists according to certain rules. ranges. Let's see some examples and then discuss them. 1 2 3 4 5 6 7 8 9 10 Probably the simplest way is by using
ghci > [1 , 2 .. 20] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20] ghci > [1 .. 20] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20] ghci > [1 , 3 .. 15] [1 ,3 ,5 ,7 ,9 ,11 ,13 ,15] ghci > [1 , 7 .. 30] [1 ,7 ,13 ,19 ,25] ghci > [3 , 2 .. -10] [3 ,2 ,1 ,0 , -1 , -2 , -3 , -4 , -5 , -6 , -7 , -8 , -9 , -10]
12
not
work.
ghci > [1 , 2, 4 , 8 .. 128] -- nope ghci > [1 .. 39 , 40] -- not this , either
It's pretty obvious: these ranges generate sequences where the dierence between consecutive terms is constant (arithmetic progressions). They always go like this:
[a, a+1 .. n]
Furthermore, only arithmetic progressions are possible using ranges. including negative or noninteger 1 2
14 ones.
15 . If you do
Ctrl-C
to stop it.
1 2
6, 7 , 8 , 9, 25 , 26 , 27 , 43 , 44 , 45 , 61 , 62 , 63 , 79 , 80 , 81 , 97 , 98 , 99 ,
15 , 16 , 17 , 18 , 19 , 33 , 34 , 35 , 36 , 37 , 51 , 52 , 53 , 54 , 55 , 69 , 70 , 71 , 72 , 73 , 87 , 88 , 89 , 90 , 91 , 104 , ^ CInterrupted .
20 , 38 , 56 , 74 , 92 ,
21 , 39 , 57 , 75 , 93 ,
lazy,
printing all the elements of an innite list (see above) we should be in the clear. We are already familiar
take,
1 2 3 4 5 6
ghci > take 20 [1..] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20] ghci > take 5 [13 , 26 ..] [13 ,26 ,39 ,52 ,65] ghci > take 11 [1 , -2 ..] [1 , -2 , -5 , -8 , -11 , -14 , -17 , -20 , -23 , -26 , -29]
We immediately notice that the computations have ended, so clearly Haskell didn't evaluate the entire innite list. In fact, when we learn more about functions, we'll see exactly how laziness works Also, take note: ranges aren't limited to numbers.
16 .
There
are three functions we have omitted from 2.2.2, and they will make it more readable. Additionally, they have
14 15 16
With decimals. Disclaimer: we won't actually print innitely many numbers. It's not unlike if-else in other languages if the statement is true, the
else
13
cycle
take
elements.
replicate
1 2 3 4 5 6
ghci > take 10 ( repeat 5) [5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5] ghci > take 10 ( cycle [5 , 4]) [5 ,4 ,5 ,4 ,5 ,4 ,5 ,4 ,5 ,4] ghci > replicate 10 4 [4 ,4 ,4 ,4 ,4 ,4 ,4 ,4 ,4 ,4]
Warning! Do not confuse
repeat
and
cycle
1 2 3 4
ghci > take 10 ( repeat [5 , 4]) [[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4] ,[5 ,4]] ghci > take 10 ( cycle [5 , 4]) [5 ,4 ,5 ,4 ,5 ,4 ,5 ,4 ,5 ,4]
ghci > [ x | x <- [1..20] ] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20] ghci > [ x | x <- [1..20] , even x ] [2 ,4 ,6 ,8 ,10 ,12 ,14 ,16 ,18 ,20] ghci > [ x | x <- [1..20] , x > 6 ] [7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20] ghci > [ x | x <- [1..20] , even x , x > 6 ] [8 ,10 ,12 ,14 ,16 ,18 ,20] ghci > [ x | x <- [1..20] , even x , x > 6, odd x ] [] ghci > [ a ++ b | a <- [" Haskell " , "C "] , b <- [ " syntax " , " types " ] ] [" Haskell syntax " ," Haskell types " ,"C syntax " ," C types "] ghci > [ x + 3 | x <- [1 , 6 .. 30] ] [4 ,9 ,14 ,19 ,24 ,29] ghci > [ x + 3 | x <- [1 , 6 .. 30] , even x ] [9 ,19 ,29] ghci > [ a ++ " is fun !" | a <- [" Haskell " , " Perl " , "C " , " Lisp "] ] [" Haskell is fun !" ," Perl is fun !" ,"C is fun !" ," Lisp is fun !"]
Anyone who's seen and understood mathematical set comprehensions can just skim the rest of the section. 2.3.2 is worth reading carefully, though. List comprehensions have two components (let's take
as an example):
The left hand-side contains the expression to be evaluated (in our case, The right hand-side has:
14
is extracted:
A list of predicates (lters) that must be satised (in this case, we have only one):
In order to understand better, let's manually calculate the above comprehension, step by step. 1. Find the base list:
2. Take the rst element from the base list and call it
3. Check the truth value of the predicates (in this case, only one): 4. If
all
the predicates are satised, evaluate the left hand-side expression for
result list. 5. Do the above steps for all elements in the base list. Voil: the result is
[2, 6].
It's important to note that internally, Haskell does things a little dierently.
ghci > [ 10* a + b | a <- [1..3] , b <- [1..3] ] [11 ,12 ,13 ,21 ,22 ,23 ,31 ,32 ,33] ghci > [ x * y | x <- [2 , 4, 6] , y <- [10 , 100 , 1000] ] [20 ,200 ,2000 ,40 ,400 ,4000 ,60 ,600 ,6000] ghci > [ x * y | x <- [1..4] , y <- [1..3] , even (x + y ) ] [1 ,3 ,4 ,3 ,9 ,8] ghci > [ x + y | x <- [3..6] , y <- [2 , 4 , 8] , x <= y ] [7 ,11 ,8 ,12 ,13 ,14]
Because a list comprehension is an expression, we can put it in the left hand-side of another one comprehensions inside comprehensions.
1 2 3
ghci > let xss = [[1 , 2 , 3 , 4, 5] , [4 , 5, 6, 7] , [7 , 8 , 9, 10]] ghci > [ [ x | x <- xs , x >= 5 ] | xs <- xss ] [[5] ,[5 ,6 ,7] ,[7 ,8 ,9 ,10]]
Moreover, instead of specifying an upper bound in a base list, we can
take
1 2
ghci > take 5 [ a | a <- [1..] , b <- [1.. a ], c <- [1.. b], a ^2 == b ^2 + c ^2 ] [5 ,10 ,13 ,15 ,17]
There are a few catches, however, some very serious.
1 2 3 4
ghci > take 20 [ x | x <- [1..] , x < 10 ] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9^ CInterrupted -- this would never finish ghci > take 5 [ x | x <- [1..] , x < 10 ] [1 ,2 ,3 ,4 ,5] -- this works fine because Haskell is lazy
Warning! Make sure Haskell can nd at least as many items as you
take. x = 2
in the
Some problems are harder to spot without running the code. For instance, Haskell never tries following example, because it has plenty of
ys
to choose from.
15
1 2
ghci > take 20 [ x * y | x <- [1..] , y <- [1..] ] [1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20]
To repeat, Haskell tries all the values from the
last
one unbounded base list, because it will either not give us what we want (see above) or run indenitely (see below). Actually, there is a mountain of theory on this issue, such as this paper (advanced content). 1 2 3 4
17
ghci > take 10 [ x * y | [1^ CInterrupted . -- bad ghci > take 10 [ x * y | [1 ,2 ,4 ,3 ,6 ,9 ,4 ,8 ,12 ,16]
x <- [1..] , y <- [1..] , y <= x ] idea , runs indefinitely x <- [1..] , y <- [1.. x ] ] -- do this instead
Mastering all the subtleties of list comprehensions takes a lot of time and experience, so let's move on. We'll learn as we go.
length of a list. We'll need to apply our knowledge of list functions here, namely 1 2
sum.
The
1 2 3 4 5
-- File : comprefunctions . hs length ' xs = sum [ 1 | _ <- xs ] vowels string = [ c | c <- string , c `elem ` " aeiou " ] removeVowels string = [ c | c <- string , c ` notElem ` " aeiou " ] allSums xs ys = [ x + y | x <- xs , y <- ys ] ghci > length ' [2 , 4 .. 10] 5 ghci > length ' [] 0 ghci > vowels " hello world " " eoo " ghci > removeVowels " hello world " " hll wrld " ghci > allSums [1 , 2, 3] [4 , 5] [5 ,6 ,6 ,7 ,7 ,8]
Functions and lists have a lot of power. We'll be using them extensively throughout this book (and even outside it) so it's better to take our time and make sure we understand as much as we can at this point. Things are only going to get harder as we advance.
1 2 3 4 5 6 7 8 9 10
17
16
(Cale)
Fortunately, Haskell has a strong type system. That means that however similar their internal representations are, the compiler won't allow us to perform illogical calculations on them, such as multiplying an integer with a boolean. This may seem restrictive (and it sometimes is), but it helps avoid certain types of errors (type errors). Moreover, Haskell features static typing, which means all types are known at compile-time so if the program has a type error, it won't even compile. As an added bonus, Haskell has type inference, so we don't need to manually specify the type of everything we use. Basically, the compiler can gure out on its own that In GHCi, we can use 1 2 3 4 5 6 7 8 9 10 11
is a number or
"hello"
is a string .
:t
ghci > :t 'a ' 'x ' :: Char ghci > :t " abcd " -- same as ['a ', 'b ' ,'c ','d '] " xxx " :: [ Char ] ghci > :t 'a ': 'b ': 'c ': 'd ' :[] -- same as " abcd " 'a ': 'b ': 'c ': 'd ' :[] :: [ Char ] ghci > :t False False :: Bool ghci > :t " hello " == " world " -- returns False " hello " == " world " :: Bool
We know that
[]
[Char]
are self-explanatory. This is just a very short example we'll be seeing more in the future. We also immediately notice that all types begin with a capital letter. This is the reason why variable and function names are lowercase . Below is a recap of the most widely used types in Haskell. We'll be running into these all the time.
1 2 3 4
'z'
is
'a' + 25,
Imagine working on a long, dicult physics problem asking for some velocity but after hours of calculations, the result is in kilograms. That can't be good. It can even deduce more complex types just as easily. The capitalization technique used for functions in Haskell is informally named
camelCase.
17
Int
231
and
231 1.
Int.
can be faster than
Double
Double
Float.
True
or
False. 1
and
won't work.
If we try to mix wrong types, Haskell throws a type error. It usually looks like this:
ghci > 3 + 'a ' < interactive >:1:1: No instance for ( Num Char ) arising from the literal `3' Possible fix : add an instance declaration for ( Num Char ) In the first argument of `(+) ', namely `3' In the expression : 3 + 'a ' In an equation for `it ': it = 3 + 'a '
Basically GHCi tells us that it doesn't know how to add
An extremely
The type declarations make functions much more expressive. Although Haskell could have inferred by itself what the types of the functions are (like in the 2.1.3 and 2.3.3 examples), we chose to give explicit type declarations to illustrate the method. In type declarations the parameters (and the return type) are separated by
->,
-- File : functions2 . hs triple :: Int -> Int triple x = 3 * x strangeAddition :: Int -> Int -> Int strangeAddition x y = x + triple y squareTwo :: Double -> Double -> Double squareTwo x y = (x + y) ^2 vowels :: [ Char ] -> [ Char ] vowels word = [ c | c <- word , c `elem ` " aeiou " ] sumLists :: [ Int ] -> [ Int ] -> [ Int ] sumLists xs ys = [ x + y | x <- xs , y <- ys ]
5
Problem Z
18
Warning! The parameters and the return type are not dierentiated all are separated by
->.
In fact, type declarations give us so much information, that we can even deduce what a function does simply from its type declaration. Let's take
an integer. We can reasonably infer that the function takes the string and performs some sort of counting (such as nding out the total length or counting all the spaces) or other calculation (such as a hash function). Indeed,
of the letters
f is dened like so: f xs = [ 1 | x <- xs, x `elem` "abc" ]. The function counts all occurences a, b, and c in a given string, so our assessment was spot-on.
Because of this tremendous advantage, we'll be giving type declarations to (almost) every function we write from now on. Oh, and just so we don't forget. If we have two functions with the same type declarations, we don't need to repeat ourselves we separate the function names with commas in their type declaration. 1 2 3 4 5
-- File : functions2 . hs ( CONTINUED ) sum1 , sum2 :: Int -> Int -> Int -> Int sum1 x y z = x + y + z sum2 x y z = x + y - z
3.2. Polymorphism
3.2.1. Type Variables
Until now, we've dened functions of type
Int -> Int or [Char] -> Int. But what about functions like head? If we give head a type declaration of [Int] -> Int, for example, it will work only with integers. But head works with basically every type of element. So what is head's type? ghci > :t head head :: [a ] -> a
1 2
is what we call a
type variable.
doesn't require specic behavior out of its parameters (unlike that can be equated), we can use
a6
==,
head
the same
type.
polymorphism :
whenever we use a type variable, we indicate that the function does not expect
3.2.2. Typeclasses
We've seen some of the most specic type signatures (like most general (for example, For this, we need typeclasses. Typeclasses group types with a common behavior. Each internal denition of a typeclass contains a
Int -> Int or Char -> Int -> Bool) and the [a] -> a, [a] -> [a] -> [a]), but what if we require something in between?
collection of functions that must work for all members of that typeclass. It's pretty simple really. Typeclasses are presented in depth in B.1 (strongly recommended reading). explain how they but In the following we'll try to
Integral
interact.
Num
and
Integral. Num
a, b, c
etc.
only integers.
It doesn't need to have only one letter, but for conciseness, we'll use
19
Num:
Integral
it is more specic. The more specic a typeclass, the more operations are
div
(integer division;
Num supplies, among others, +, -, * and abs. Integral oers, / in other languages) and mod (modulo; % in other languages).
8 of numbers. But as soon as we perform an
in addition,
If we just write
20
or
30,
function on them, they (and the result of the operation) can no longer be We'll get round to 1 2 3 4 5 6 7 8
=>
in a few moments.
ghci > :t 20 20 :: Num a => a ghci > :t 30 30 :: Num a => a ghci > :t 20 `div ` 5 20 `div ` 5 :: Integral a => a ghci > :t 20 `mod ` 30 20 `mod ` 30 :: Integral a => a
This is the gist of typeclasses and polymorphism: they group common behavior so we can make very general functions. If we make a
sort
function, we can be certain that it won't only work with lists of numbers, but
also with strings or anything else that can be ordered. At this point, it's a good idea to go through the typeclasses described in B.1. They're very useful.
ghci > :t (+) (+) :: Num a = > a -> a -> a ghci > :t (^) (^) :: ( Num a , Integral b) => a -> b -> a ghci > :t pi pi :: Floating a = > a ghci > :t show show :: Show a => a -> String
It seems polymorphic functions really do use the of the function is right after the
=>
a lot.
constraint. In the rst example, it tells the compiler (and us) that
=>.
=>
is a class
When we read such a denition, we usually do it (somewhat) from right to left. We shall use
as an example.
(^) ::
is the name of the function. In this case it's surrounded by parentheses because it consists only of
symbols. means has type of now we jump to the bit after the
=>.
a -> b -> a
means the function takes a parameter of a type (a), a parameter of another type (b) and
7 8 9
is
intuitively true.
We've avoided using kind to the point of repeating ourselves. This is not due to lack of vocabulary: in Haskell, something dierent. Kinds are explained in [XREF] (advanced topic).
kind
means
We can also have multiple class constraints by surrounding them in parentheses and separating them with commas, like in
(^).
20
(Num a, Integral b)
10 integer .
is an
Now we'll apply our newly-gained knowledge to make our functions more general. We'll recycle examples from 2.1.3, 2.3.3, and 3.1.2. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
-- File : polyfunctions . hs triple :: Num a = > a -> a triple x = 3* x strangeAddition :: Num a => a -> a -> a strangeAddition x y = x + triple y c :: Num a => a c = 4 length ' :: Num a => [ b] -> a length ' xs = sum [1 | _ <- xs ] vowels :: [ Char ] -> [ Char ] vowels word = [ c | c <- word , c `elem ` " aeiou " ] sumLists :: Num a => [ a] -> [ a] -> [a ] sumLists xs ys = [ x + y | x <- xs , y <- ys ]
A great thing about Haskell is that if our type denitions are wrong (i.e., they are incompatible with the function itself ), an error is thrown. Apart from the obvious advantage, this means we can cheat and let Haskell infer the type for us, then copy-paste it in our le.
1 2 3
ghci > let spaces xs = sum [ 1 | x <- xs , x == ' ' ] ghci > :t spaces spaces :: Num a = > [ Char ] -> a -- File : polyfunctions . hs ( CONTINUED ) spaces :: Num a = > [ Char ] -> a spaces xs = sum [ 1 | x <- xs , x == ' ' ]
1 2 3 4
3.2.4. Drawbacks
We've seen how we can make our programs more readable and reliable by adding type denitions. The good news is that we can't accidentally add centimeters and inches. The bad news is that we can't add an integer and a oating point.
What
4 + 5.1,
ghci > 4 + 5.1 9.1 ghci > (4 :: Int ) + (5.1 :: Float ) < interactive >:1:15: Couldn 't match expected type `Int ' with actual type ` Float '
10
It can be any one of the 7 types of integer Haskell has.
21
7 8 9
In the second argument of `(+) ', namely `(5.1 :: Float ) ' In the expression : (4 :: Int ) + (5.1 :: Float ) In an equation for `it ': it = (4 :: Int ) + (5.1 :: Float )
It seems that it all blows up if we force the types. The above error tells us, quite clearly, that it expected to be an
Int
rather than a
Float.
5.1
that we previously mentioned polymorphic constants. We can easily check if this is the case here. 1 2 3 4 5 6
ghci > :t 4 4 :: Num a => a ghci > :t 5.1 5.1 :: Fractional a => a ghci > :t (4 + 5.1) (4 + 5.1) :: Fractional a => a
Aha! So (Float, in
4 can take any number type (Int, Complex, Rational, Float, Double etc.), but 5.1 is a fractional Double etc.). Naturally, adding them means that 4 can have only the types 5.1 can have, so anything Fractional12 .
Right now, things may seem confusing (and rightfully so). The most important thing to remember here is to make type declarations as general as possible, but not more general. In bullet points:
If we're not sure of a type, we should leave it blank. The compiler always infers types better than the user
Some food for thought: what happens if a typeclass has the same name as a type? So, for example, we have
Derp
one? Well, they're logically dierent: one is a type, the other a typeclass. It doesn't matter if both have Does anyone ever confuse Jack the actor with Jack the movie character terms, we say that they have dierent the namespace.
kinds
15 ? In technical
ever confuse them, and as it happens, it's a pretty frequently used technique: we don't want to... pollute
The addition operator (+) is of the type Actually, it should look like the same thing.
but because
Fractional
is included in
Num,
it's
Sometimes we want to avoid that. For example, maybe we want a function that can only triple integers so we don't accidentally rounding errors. Unless, of course, it's released software type denitions are half the documentation. Or for physicists,
the acceleration.
22
6 7 8
(:) :: a -> [a ] -> [a ] ghci > :t (++) (++) :: [a ] -> [a ] -> [a]
Even if we don't know anything about lists, from the above piece of code we can draw two very important conclusions:
No matter how long a list is, its type is the same. This makes them essentially variable in length we have do-it-all functions that can lengthen (:, regardless of length.
++
drop
Both
and
++
take identical types as parameters, so there's no way we can get away with adding a
dierent type of element to a list. This translates into our current knowledge of lists: variable length and homogeneity. It reinforces the idea that we can learn a great deal simply by analyzing types.
seem like a lot, but we can extract a wealth of information from the little we know. First, let's see if we got the syntax right and try various things to see if they work. 1 2 3 4 5 6 7 8 9 10 11 12 13 14
ghci > (4 , 5, 6) (4 ,5 ,6) ghci > (10 , 2, 3, 3) (10 ,2 ,3 ,3) ghci > (85 , " Hello ") (85 , " Hello ") ghci > ( 'a ', " Haskell " , 15 , " never " , " easy ") ( 'a ' ," Haskell " ,15 , " never " ," easy ") ghci > () () ghci > ( 'a ') 'a ' ghci > (20) 20
Let's draw some partial conclusions about tuples:
not
necessarily homogenous.
().
17 .
ghci > :t (4 , 5 , 6) (4 , 5, 6) :: ( Num t1 , Num t2 , Num t) = > (t , t1 , t2 ) ghci > :t (10 , 2, 3 , 3) (10 , 2 , 3, 3) :: ( Num t1 , Num t3 , Num t2 , Num t) => (t , t1 , t2 , t3 ) ghci > :t (85 , " Hello ")
16 17
For the record, that's not a new feature. That's pretty obvious all we did is surround them with parentheses.
23
6 7 8 9 10 11 12 13 14 15
(85 , " Hello ") :: Num t => (t , [ Char ]) ghci > :t ( 'a ' , " Haskell " , 15 , " never " , " easy ") ( 'a ' , " Haskell " , 15 , " never " , " easy " ) :: Num t => ( Char , [ Char ] , t , [ Char ] , [ Char ]) ghci > :t () () :: () ghci > :t ( 'a ') ( 'a ') :: Char ghci > :t (20) (20) :: Num a => a
So the type of the tuple contains the types of all the elements inside it. This means:
Tuples have an essentially xed length An empty tuple is its own type:
18 .
()
is of type
().
We've also inadvertently learned that type denitions can be split across multiple lines (as long as the next lines are indented slightly to the right).
ghci > ( ,) < interactive >:1:1: No instance for ( Show ( a0 -> b0 -> ( a0 , b0 ) )) arising from a use of ` print ' Possible fix : add an instance declaration for ( Show ( a0 -> b0 -> ( a0 , b0 ) )) In a stmt of an interactive GHCi command : print it
The error says: the type of
(,), which is a0 -> b0 -> (a0, b0) (a function19 ) is not a member of the Show
safe to say that it creates a tuple from its two parameters
typeclass (which is no surprise seeing we can't print functions). So what does we have 1 2 3 4 5 6
ghci > ( ,) 5 6 (5 ,6) ghci > ( ,) 123 " abc " (123 , " abc ") ghci > ( , ,) 'a ' 16 " ddx " ( 'a ' ,16 , " ddx ")
Problem Z.
snd,
(5, 6).
(,)
Another thought experiment let's imagine that somebody told us about two useful functions: but they didn't mention what they do. As always, we want to check their types rst.
fst
and
18 19 20
We can write functions to add an element to a tuple of a specic size (and type) but never universal ones that work on all of them. One that takes two types and returns a tuple which contains those types. 2-tuples (those made using
(,))
are usually called pairs (or sometimes doubles), 3-tuples are triple(t)s etc.
24
1 2 3 4
ghci > :t fst fst :: (a , b) -> a ghci > :t snd snd :: (a , b) -> b
Now it's clear.
fst
snd,
the second.
1 2 3 4 5
ghci > fst (5 , "a" ) 5 ghci > snd (5 , "a" ) "a " ghci > fst (1 , 2, 3) -- whoops , error
Warning!
fst
and
snd
only work on pairs. There are no built-in functions for triples or larger.
3.3.4. Applications
Tuples are especially useful in conjunction with functions or list comprehensions, namely when we want to return multiple things. We now go back to some of the 2.3.2 examples, and try to improve them. 1 2 3 4 5 6
ghci > [ (a , b) | a <- [1..3] , b <- [1..3] ] [(1 ,1) ,(1 ,2) ,(1 ,3) ,(2 ,1) ,(2 ,2) ,(2 ,3) ,(3 ,1) ,(3 ,2) ,(3 ,3) ] ghci > [ (x , y , x + y) | x <- [1..4] , y <- [1..3] , even (x + y ) ] [(1 ,1 ,2) ,(1 ,3 ,4) ,(2 ,2 ,4) ,(3 ,1 ,4) ,(3 ,3 ,6) ,(4 ,2 ,6) ] ghci > take 5 [ (a , b , c) | a <- [1..] , b <- [1.. a ], c <- [1.. b], a ^2 == b ^2 + c ^2 ] [(5 ,4 ,3) ,(10 ,8 ,6) ,(13 ,12 ,5) ,(15 ,12 ,9) ,(17 ,15 ,8) ]
So far, so good. Tuples seem to be okay for trivial uses, but where they really work wonders is in larger, more complex programs. A classic example is splitting a list in order to work on both parts simultaneously. We'll look deeper into this in [XREF] and [XREF].
1 2 3 4 5
ghci > let splitHead xs = ( head xs , tail xs ) ghci > splitHead [1 , 5, 3 , 2, 6] (1 ,[5 ,3 ,2 ,6]) ghci > splitHead [] (*** Exception : Prelude . head : empty list
Of course, we can't perform called
splitAt
1 2
splitAt also takes an Int apart from the list, and returns a pair of lists so it's logical to think
1 2 3 4
ghci > splitAt 5 [1..10] ([1 ,2 ,3 ,4 ,5] ,[6 ,7 ,8 ,9 ,10]) ghci > splitAt 1 [2 , 3 , 5, 8] ([2] ,[3 ,5 ,8])
25
5 6 7 8 9 10 11 12
ghci > splitAt 0 [2 , 3 , 5, 8] ([] ,[2 ,3 ,5 ,8]) ghci > splitAt ( -1) [2 , 3 , 5, 8] ([] ,[2 ,3 ,5 ,8]) ghci > splitAt 5 [1 , 2] ([1 ,2] ,[]) ghci > splitAt 1 [] ([] ,[])
That's it for now! We'll return to types later on, but our next big step is mastering functions with advanced syntax and everything.
26
Part II.
27
4. Exploring Syntax
Uninformed people believe that syntax is the hardest part of learning a language.
(kmc)
-- File : useless - dict . hs engGer :: [ Char ] engGer word = if else if else if else if else if else if else " I -> [ Char ] word == " one " word == " two " word == " three " word == " four " word == " five " word == " six " don ' t know what then then then then then then " ++ " eins " " zwei " " drei " " vier " " fnf " " sechs " word ++ " means ."
That works perfectly, apart from the fact that it looks awful and contains lots of superuous information, such as the rst
if
or the second
if
or the third
if...
-- File : patterns . hs engGer engGer engGer engGer engGer engGer engGer engGer :: [ Char ] " one " = " two " = " three " = " four " = " five " = " six " = word = -> [ Char ] " eins " " zwei " " drei " " vier " " fnf " " sechs " " I don 't know what " ++ word ++ " means ."
1 2
=s
Bear with us the rst examples are really boring. ...but it's still inecient to write a dictionary like that.
28
4. Exploring Syntax
In the second example we have used something called pattern matching. Essentially, Haskell looks at each of the patterns (from top to bottom) , and if one works, it will evaluate the corresponding function body. It's pretty simple if we think about it. To clarify, the syntax looks like: 1 2 3 4 5 6
-- Syntax : pattern matching function pattern1 = result1 function pattern2 = result2 function pattern3 = result3 function pattern4 = result4 ...
If we're not careful, our pattern matching can fail. This happens mostly when we don't cover all our angles we forget to consider a case.
1 2 3 4 5 6
-- File : patterns - wrong . hs intToString intToString intToString intToString :: Int -> [ Char ] 1 = " one " 2 = " two " 3 = " three " 1, 2,
and
This example is boring, but it illustrates the issue quite well. It's obvious that all cases except a corresponding pattern to match the input.
are missing, but in real life things may not be so straightforward. GHCi throws an error when it can't nd
These errors are particularly dangerous because the compiler can't nd them right away: it has to be given an incorrect input, and by that time it might be too late. We
can
use
GHCi will warn us on non-exhaustive patterns, but this isn't 100% guaranteed better to check personally. 1 2 3 4
ghci > intToString 3 " three " ghci > intToString 20 *** Exception : dontbother . hs :(4 ,1) -(6 ,23) : Non - exhaustive patterns in function Main . intToString
Warning! Make sure all possible cases are covered in pattern-matching.
The obvious solution is to introduce some sort of catch-all pattern.
1 2 3 4 5 6 7
-- File : patterns - wrong . hs ( FIXED ) intToString intToString intToString intToString intToString :: Int -> [ Char ] 1 = " one " 2 = " two " 3 = " three " n = " I don ' t know about " ++ show n
In this case, everything is well. The program won't crash when we give an unexpected input, but it won't do anything useful either. As we progress, we'll learn how to deal with increasingly complex scenarios. 1 2
If we move
because
word
29
4. Exploring Syntax
4.1.2. Applications
We don't actually want to use pattern matching just as a gloried if-else. Where it really shines is in matching
fst
on a triple. We can't do that, but at this point we know very well that
-- File : patterns2 . hs fst3 :: (a , b , c) -> a fst3 (x , _ , _) = x ghci > fst3 (" Mike " , " Adams " , 23) " Mike "
Now that we know it works, it's a breeze to implement the whole lot.
1 2
1 2 3 4 5 6
mathematicians
two 2D vectors. What does that mean? Basically we are given two pairs like: 1 2 3
4 multiplication is (a c, b d). Easy as pie . Before learning pattern matching, we might have done something
(a, b)
and
(c, d)
-- File : vectors . hs mulVct :: Num a = > (a , a) -> (a , a) -> (a , a ) mulVct a b = ( fst a * fst b , snd a * snd b )
It works perfectly well (we can try it), but it's not quite what we wanted. Let's arm ourselves with patterns and try again.
1 2 3
1 2 3 4
ghci > mulVct (1 ,2) (3 ,4) (3 ,8) ghci > mulVct (0 ,1) (5 ,10) (0 ,10)
A word of warning: there. Because we
Num a => (a, a) -> (a, a) -> (a, a) is not the most general type denition out only multiply a with c and b with d, a and c can have dierent types from b and d.
However, in this case it doesn't make much sense vectors should be homogenous. So, even though the compiler doesn't care, we do. So here we go:
Another thing: Even though, at rst, they might seem like a good idea, lists aren't suitable as vectors because they have variable length.
30
4. Exploring Syntax
1:2:3:[]
and
1:[2, 3].
[1, 2, 3]
is the same as
1 2 3 4 5 6 7 8 9 10 11 12
-- File : cons - patterns . hs match1 :: ( Num a ) => [a] -> String match1 [x , y , z] = " List of 3 numbers with sum " ++ show (x + y + z) match1 _ = " Nope ." match2 :: ( Num a ) => [a] -> String match2 (x :y: z :[]) = " List of 3 numbers with sum " ++ show ( x + y + z ) match2 _ = " Nope ." match3 :: ( Num a ) => [a] -> String match3 (x :[y , z ]) = " List of 3 numbers with sum " ++ show (x + y + z) match3 _ = " Nope ."
We will say this only once: patterns made of multiple bits must be surrounded by parentheses. necessary, while
([x, y])
(x:y:[])
is
is not.
All three functions above do the exact same thing. Although this may be interesting, in our case, their main disadvantage is that they match only lists of length 3. It's not particularly useful, but what it illustrates is the equivalence of certain notations. Before continuing, we must note that pattern matching trying it with 1 2 3 4
++
For example,
-- File : cons - patterns - wrong . hs match4 :: ( Num a ) => [a] -> String match4 ([ x ,y ] ++ [z ]) = " List of 3 numbers with sum " ++ show ( x + y + z ) match4 _ = " Nope ."
Although it certainly looks logical to us, the compiler doesn't think the same.
1 2 3 4 5
cons - patterns - wrong . hs :3:9: Parse error in pattern : [x , y ] ++ [z ] Failed , modules loaded : none .
The reason it works with
++
is that
creates (
cons tructs)
++
is just a function that happens to operate on lists. We've seen how to create pattens that exactly match the input (engGer
n).
one).
(_, y, _)).
Now we
We can't bind all of the elements, individually, to variables because we don't know how many of them there are. What we can do is, say, name the rst element of the list, say, 1 2 3
xs.
-- File : cons - patterns . hs ( CONTINUED ) describe :: ( Show a) => [ a] -> String describe ( x: xs ) = "A list with the first element " ++ show x ++ " and " ++ show ( length xs ) ++ " other elements ."
5
31
4. Exploring Syntax
x:xs
is
and
xs
is
1:[2, 3, 4, 5]
so it ts the
ghci > describe [1..5] "A list with the first element 1 and 4 other elements ." ghci > describe " hello , world " "A list with the first element 'h ' and 11 other elements ." ghci > describe [] *** Exception : cons - patterns . hs :3:1 -113: Non - exhaustive patterns in function describe
What seems to be the problem? If we look closely, element of
[],
so
There is no rst
can't be matched to it. Thus the whole pattern fails (half wrong is all wrong). We can
-- File : cons - patterns . hs ( CONTINUED ) ( FIXED ) describe :: ( Show a) => [ a] -> String describe [] = " An empty list ." describe ( x: xs ) = "A list with the first element " ++ show x ++ " and " ++ show ( length xs ) ++ " other elements ." ghci > describe [] " An empty list ."
Incidentally, the
1 2
head
function in
Prelude
1 2 3 4
-- File : ourhead . hs head ' :: [ a] -> a head ' (x:_ ) = x head ' [] = undefined
This
undefined
undefined.
head'
ghci > head ' [4 , 4] 4 ghci > head ' [] *** Exception : Prelude . undefined
Just a quick reminder: if we want to have custom error messages, we can take a look at B.2.3.
error,
explained in
x:xs
the string into a head and a tail and then puts it back together again. It's inecient. 1 2 3 4
-- File : as - patterns . hs f :: String -> String -- String is the same as [ Char ] f " " = " This is an empty string . " f ( x: xs ) = " The string " ++ x: xs ++ " has the first character " ++ [x]
Notice the dierence (below) when using as patterns by writing we can reference the whole pattern by using the name
all,
all@(x:xs)
instead of simply
x:xs
(x:xs)
32
4. Exploring Syntax
1 2 3 4
-- File : as - patterns . hs ( FIXED ) f :: String -> String -- String is the same as [ Char ] f " " = " This is an empty string . " f all@ (x: xs ) = " The string " ++ all ++ " has the first character " ++ [x]
Another example:
1 2 3 4
error
undefined
1 2 3 4
-- File : as - patterns2 . hs ( FIXED ) split3 :: [a ] -> (a , a , [ a ]) split3 list@ (x: y: ys ) = (x , y , list ) split3 _ = error " split3 : list too short "
As
list.
name@horriblyLongPattern will bind the entire pattern to name, list@(x:y:ys) spares us the need to write x:y:ys again.
ghci > let stuff = [(4 , 5) , (8 , 3) , (2 , 2) , (6 , 1) , (3 , 2) ] ghci > [ a * b | (a , b) <- stuff ] [20 ,24 ,4 ,6 ,6] ghci > [ a + b | (a , b) <- stuff , even a , odd b ] [9 ,11 ,7] ghci > [ [a , b] | (a , b ) <- stuff ] [[4 ,5] ,[8 ,3] ,[2 ,2] ,[6 ,1] ,[3 ,2]]
This time, if a pattern fails, it will just move on to the next element.
1 2 3 4 5
ghci > let newstuff = [[4 ,5 ,6] , [7 ,8] , [9 ,10 ,11]] ghci > [ a + b*c | [a ,b ,c] <- newstuff ] [34 ,119] ghci > [ 2* a | [ a] <- newstuff ] []
If a pattern's
type
1 2 3 4 5 6 7 8
ghci > [ x + y | (x , y) <- [(1 , 1, 1) , (2 , 2 , 2) ] ] < interactive >:1:11: Couldn 't match expected type `(t0 , with actual type `(t3 , In the pattern : (x , y ) In a stmt of a list comprehension : In the expression : [ x + y | (x , y )
6
Haha.
33
4. Exploring Syntax
Warning! While failing patterns can be excused, using the wrong type
always
results in an error.
not
-- File : guards . hs numberSize :: ( Ord a , Fractional a) => a -> String numberSize x | x < 0.1 = " Small " | x < 1 = " Small - ish " | x < 10 = " Okay " | x < 100 = " Large " | otherwise = " Huge ! "
In the above example, we tried to estimate the size of a given number using adjectives like
Huge!.
like.
Small-ish
This is not terribly mature, but shows how these things (which, by the way, are called
guards )
and look
Guards are basically a replacement of if-else trees. They are separated by separate lines for readability. the result (Okay).
|7 and usually neatly aligned on They consist of a boolean expression (such as x < 10), followed by =, and then
The rst boolean to be
Just like patterns, guards are checked from top to bottom. evaluated (and Haskell as writing
True,
won't
After this huge block of text, we should refresh our eyes by looking at some code. We've implemented our own versions of 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
and
compare
in a variety of styles.
-- File : guards . hs ( CONTINUED ) max2 :: Ord a => a -> a -> a max2 x y | x <= y = y | otherwise = x min2 :: Ord a => a -> a -> a min2 x y | x <= y = x | otherwise = y abs2 :: ( Num a , Ord a ) => a -> a abs2 x | x < 0 = -x | otherwise = x abs2 ' :: ( Num a , Ord a ) = > a -> a abs2 ' x | x < 0 = -x abs2 ' x = x compare2 :: Ord a => a -> a -> Ordering x ` compare2 ` y | x == y = EQ
7 8 9
These things are called pipes. We've seen them in list comprehensions but here they do entirely dierent things. It's not mandatory but highly recommended. If Haskell reaches the end of the guards without meeting an checks the next pattern (as in pattern matching). If no corresponding patterns are found, an error is thrown. A little more restrictive than the ocial implementation (requires
otherwise,
it
Ord).
34
4. Exploring Syntax
20 21 22 23 24 25 26
| x <= y = LT | otherwise = GT compare2 ' :: Ord a = > a -> a -> Ordering compare2 ' x y | x == y = EQ | x <= y = LT | otherwise = GT
All of the above are valid, but some are more readable than others. From top to bottom: 1. 2. 3.
has a pretty standard style we've seen this one above, and it's very readable. is at the other end of the spectrum: putting guards in a single line is not a good idea. puts the guards immediately to the right of the function and starts them on the same line. Also
4.
abs2'
abs',
but
uses a totally dierent layout. Not usually recommended, but in some cases it looks better than the alternatives. 5.
6.
compare2':
this is very bad. It works just ne, but it looks horrendous. We also notice that the guards
At the end of the day, it's not a big deal which style we choose possible, but not if it means sacricing readability.
Let's try some more examples with guards. Say we want to make a drink calculator. It shows us how sober somebody is, given the blood alcohol concentration 1 2 3 4 5 6 7
12 .
-- File : drink - calc . hs drink :: ( Ord a , Fractional a ) = > a -> String drink bac -- Blood Alcohol Concentration | bac < 0.03 = " You ' re as sober as can be expected ." | bac < 0.08 = " You can drive , but it 's a bad idea ." | bac < 0.10 = " Your reasoning is out the window . " | otherwise = " Stop drinking . "
This is kinda lengthy, and not very useful, but we'll perfect it as we move along. For now, let's give it a try.
1 2 3 4 5 6 7
ghci > drink 0.07 " You can drive , but it 's a bad idea . " ghci > drink (4/30) " Stop drinking ." ghci > import Data . Ratio -- let 's try rationals , too ghci > drink (1 % 5) " Stop drinking ."
One does not simply know the blood alcohol concentration it needs to be calculated. Fortunately, there is a simple formula, where
13
10 11 12 13
If the
Except the everything-on-a-single-line method (min2) and the one randomly indented (compare2') we run from them like the plague. We've found this information on the internet, so it's not the most precise calculator out there.
starts at the very beginning of the line, Haskell treats it as a new function denition.
Warning! Excessive alcohol consumption can be hazardous to your health. Driving vehicles or operating heavy machinery should not be done under the inuence of this dangerous chemical. Drink responsibly. Drive safely. This message brought to you by Haskellers Anonymous.
35
4. Exploring Syntax
c=
In Haskell speak, this is
0.025 N 0.035 N
we want it to do, this is yet another reminder that we can jam the if-else anywhere. It's better than saying
bac = if sex == "male" then n*0.025 else n*0.035 because we're not repeating
ourselves, not to mention that it's clearer. With our current knowledge of Haskell, there are two ways of doing it, neither particularly good. 1 2 3 4 5 6 7
-- File : drink - calc . hs drink :: ( Fractional a , Ord a ) = > String -> drink sex n -- Blood Alcohol Concentration | ( n * if sex == " male " then 0.025 else sober as can be expected . " | ( n * if sex == " male " then 0.025 else but it ' s a bad idea . " | ( n * if sex == " male " then 0.025 else is out the window ." | otherwise = " Stop drinking . "
If we try it out, it works:
a -> String 0.035) < 0.03 = " You ' re as 0.035) < 0.08 = " You can drive , 0.035) < 0.10 = " Your reasoning
1 2 3 4 5 6 7 8
ghci > drink " male " 4 " Stop drinking ." ghci > drink " female " 2 " You can drive , but it 's a bad idea . " ghci > drink " male " 1 " You ' re as sober as can be expected . " ghci > drink " female " 8 " Stop drinking ."
The code is, however, yucky (and that's putting it mildly). The other solution is to use another function to calculate the
bac.
1 2 3 4 5 6 7 8 9 10
-- File : drink - calc . hs ( FIXED ) bac :: ( Fractional a , Ord a ) => String -> a -> a bac sex n = n * if sex == " male " then 0.025 else 0.035 drink drink | | | | :: ( Fractional a , Ord a ) = > String -> a -> String sex n -- Blood Alcohol Concentration bac sex n < 0.03 = " You ' re as sober as can be expected . " bac sex n < 0.08 = " You can drive , but it ' s a bad idea . " bac sex n < 0.10 = " Your reasoning is out the window ." otherwise = " Stop drinking . "
We're still repeating ourselves and we've just
It still works and it's a tad shorter, but that's about it. we can do.
introduced a function that we're not going to use anywhere else. With what we know so far, there's nothing
where
bac
36
4. Exploring Syntax
1 2 3 4 5
-- File : gpa . hs gpa :: [ Int ] -> Int -> Int gpa grades final = func grades + final where func :: [ Int ] -> Int func xs = sum xs `div ` length xs
It is time to take a moment and contemplate this function. Okay, moment's over. So what do we have here? Why, a GPA calculator, of course. This one seems to do something with the grades then add it to the nal. If we only read the rst line, we don't know what does. Neither does the compiler. The
func
where
func
learned. It's easy to see what it does. The type denition tells us that it takes a list of integers and returns only one, and the body indicates it averages those numbers other grades. Pretty simple. Another thing: inside
14 . So
gpa
15 omitted ), multiple function bodies, pattern matching etc. It's just like our typical function (or name)
denition. We can even put a
where
sections we can have the usual gimmicks: type declarations (which are usually
where
inside a
where!
In fact, pattern matching inside where sections is so useful and important, it's worth giving a specic example. 1 2 3 4
-- File : stutter . hs stutter :: String -> String stutter word = [ w] ++ " -" ++ [w ] ++ " -" ++ word where ( w:_ ) = word
It's
[w],
not
things like 1 2
w because ++ takes strings, not characters. The keen reader would notice where w = head word. No matter how we write it, we should be consistent
where
bindings. Now it's time to improve our calculator (in three easy steps). This
1 2 3 4 5 6 7 8 9
bac :: ( Fractional a , Ord a ) => String -> a -> a bac sex n = n * if sex == " male " then 0.025 else 0.035 drink drink | | | | :: ( Fractional a , Ord a ) = > String -> a -> String sex n bac sex n < 0.03 = " You ' re as sober as can be expected . " bac sex n < 0.08 = " You can drive , but it ' s a bad idea . " bac sex n < 0.10 = " Your reasoning is out the window ." otherwise = " Stop drinking . "
Problems:
We're repeating ourselves. We have a function that we use nowhere else. The code is slightly confusing.
bac
in a
where
where
14 15
average or avg or something instead of func. where sections are usually short and simple. If
37
4. Exploring Syntax
1 2 3 4 5 6 7 8
drink :: ( Fractional a , Ord a ) = > String -> a -> String drink sex n | bac sex n < 0.03 = " You ' re as sober as can be expected . " | bac sex n < 0.08 = " You can drive , but it ' s a bad idea . " | bac sex n < 0.10 = " Your reasoning is out the window ." | otherwise = " Stop drinking . " where bac :: ( Fractional a , Ord a ) = > String -> a -> a bac sex n = n * if sex == " male " then 0.025 else 0.035
Problems:
We're repeating ourselves. We have a function that we use nowhere else. The code is slightly confusing.
bac's
sex
and
n,
where
sex n
is
section).
drink :: ( Fractional a , Ord a ) = > String -> a -> String drink sex n | bac < 0.03 = " You ' re as sober as can be expected ." | bac < 0.08 = " You can drive , but it 's a bad idea ." | bac < 0.10 = " Your reasoning is out the window . " | otherwise = " Stop drinking . " where bac = n * if sex == " male " then 0.025 else 0.035
Problems:
We're repeating ourselves. We have a function that we use nowhere else. The code is slightly confusing.
Finally, let's make the function easier to understand and modify by giving names to has a 1 2 3 4 5 6 7 8 9 10
0.03, 0.08
and
0.10.
This way we can be sure we understand what they mean and also easily modify them (for instance, France
0.05
drink :: ( Fractional a , Ord a ) = > String -> a -> String drink sex n | bac < soberLimit = " You ' re as sober as can be expected . " | bac < drivingLimit = " You can drive , but it ' s a bad idea ." | bac < thinkingLimit = " Your reasoning is out the window . " | otherwise = " Stop drinking . " where bac = n * if sex == " male " then 0.025 else 0.035 soberLimit = 0.03 drivingLimit = 0.08 thinkingLimit = 0.10
Problems:
We're repeating ourselves. We have a function that we use nowhere else. The code is slightly confusing.
Now we're ready to move on. Oh, and one more thing. We must align things neatly following the part, or the code might not compile or function correctly.
where
38
4. Exploring Syntax
Warning! In
where
where
-- File : cone . hs coneVolume :: Floating a => a -> a -> a coneVolume r h = baseArea * h / 3 where baseArea = pi * r ^2
-- File : cone - let . hs coneVolume :: Floating a => a -> a -> a coneVolume r h = let baseArea = pi * r ^2 in baseArea * h / 3
It seems pretty intuitive. reversed One might say let bindings are let <bindings> in <expression>, as opposed just like to
where bindings, only with the order <expression> where <bindings>. There's
much more to them, though. A mountain of examples follows (and not many words). For a start, 1 2 3 4 5 6 7 8
let
if
ghci > let a = 3 in 2 * a 6 ghci > 4 + 5 * ( let x = 5 in 2 * x ) 54 ghci > 2 + 3 * ( let e = 2.718281828 in e * (e + 1) ) 32.32201377330506 ghci > " hello " ++ ( let w = " world " in w ++ w ++ w) " hello world world world "
... and loaded from a le (just like
where
bindings,
let
1 2 3 4 5 6
-- File : cone - area . hs coneArea :: Floating a = > a -> a -> a coneArea r h = let baseArea = pi * r ^2 sideArea = let l = sqrt (r ^2 + h ^2) in pi * r * l in baseArea + sideArea
We can perform many neat tricks using
let,
such as:
1 2 3 4
inline 16
using semicolons.
ghci > let x = 4; y = 5; z = 6 in (x + y) * z 54 ghci > " Hello " ++ ( let x = " world "; y = " wide " in y ++ x) ++ " !" " Hello wide world ! "
16
Using pattern matching
39
4. Exploring Syntax
1 2 3 4 5 6
ghci > let (x , y) = (3 , 2) in y * x 6 ghci > let x:y :_ = " asdf " in y :x :[] " sa " ghci > 4 + ( let a:b :c: _ = [5 ,10..] in c - b + a) 14
1 2 3 4
ghci > [ x | x <- [1..10] , let a = 8*x , a < 50] [1 ,2 ,3 ,4 ,5 ,6] ghci > [ x: xs | x <- [ 'a '.. 'c '] , let xs = " ghj "] [" aghj " ," bghj " ," cghj " ]
Nesting them.
1 2 3 4
ghci > let x = 4 in let y = 5 in x + y 9 ghci > let a = 'h ' in let as = " ello " in a : as " hello "
When dening several variables with
let,
1 2 3 4
1 2 3 4
lets
1 2 3 4 5 6
ghci > [ x | x <- [1..10] , y < 2 , let y = x - 5] < interactive >:1:21: Not in scope : `y ' ghci > let y = 2 * x in ( let x = 4 in y + x) < interactive >:2:13: Not in scope : `x '
Additionally, things:
let bindings are very local; they are only visible where we dene them we talk more about local
let
bindings are
not
visible across guards. All these drawbacks are the result of a very simple
Prelude > let a = 3 in 2 * a 6 Prelude > a < interactive >:2:1: Not in scope : `a ' ghci > ( let b = 5 in 4 * b) + b
40
4. Exploring Syntax
7 8 9 10 11
< interactive >:3:24: Not in scope : `b ' ghci > [ x | x <- [1..10] , let c = 2*x , c < 5] ++ [ c] < interactive >:4:45: Not in scope : `c '
There is only one exception to this rule: we can omit the
in
way, the names will be visible during the entire interactive session (but not the next). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
ghci > let a = 5; b = 6 ghci > " hello world " " hello world " ghci > a + b 11 ghci > :q Leaving GHCi . ee@bt :~ $ ghci GHCi , version 7.4.1: http :// www . haskell . org / ghc / Loading package base ... linking ... done . Prelude > a + b < interactive >:2:1: Not in scope : `a ' < interactive >:2:5: Not in scope : `b '
It's time for a little discussion and recap. The
:? for help
syntax allows
one of their great advantages. The above reasons, and more, bring us to our nal point:
where
let,
let
and
where
[1, 2, 3]
1:2:3:[],
-- File : case - expr . hs tail ' :: [ a] -> [a ] tail ' [] = error " tail ': empty list " tail ' (_: xs ) = xs
We've just implemented our version of it looks with case expressions.
tail
1 2 3 4
-- File : case - expr . hs ( FIXED ) tail ' :: [ a] -> [a ] tail ' all = case all of [] -> error " tail ': empty list " (_ : xs ) -> xs
17
The biggest problem is that they won't work with guards the way we want them to.
41
4. Exploring Syntax
The syntax for case expressions is pretty much self-explanatory. A longer example, just to consolidate our knowledge: 1 2 3 4 5
-- File : case - expr2 . hs f :: Int -> String f n = case n of 1 -> " one " 2 -> " two " _ -> " many "
Of course, those can be any patterns, not just numbers. If it's not 100% clear yet, this is the syntax:
1 2 3 4 5 6
-- Syntax : case expressions ( in function definitions ) function argument = case argument of pattern1 -> result1 pattern2 -> result2 pattern3 -> result3 pattern4 -> result4 ...
We've been very careful to mention in function denitions repeatedly. That's because, technically, case
expressions make use of pattern matching, so it's not really fair to compare the two. Their main advantage is that case expressions work anywhere, just like
let
pattern matching anywhere we desire. We can put them in the middle of an expression, for example. 1 2 3 4 5
-- File : case - expr3 . hs f :: ( Show a) => [ a] -> f [] = " This list is f [ x] = " This list is f ( x:_ ) = " This list is
String empty . Sorry . " a singleton , with the element : " ++ show x longer . Its head is : " ++ show x
1 2 3 4 5
-- File : case - expr3 . hs ( FIXED ) f :: ( Show a) => [ a] -> String f xs = " This list is " ++ case xs of [] [x ]
-> " empty . Sorry ." -> "a singleton , with the element : " ++ show x (x :_) -> " longer . Its head is : " ++ show x let
bindings:
18 than the alternatives. Syntactic sugar in general oers a clearer they are ever-so-slightly less readable
exposition at the expense of power. In fact, after this chapter on syntax, we've seen many alternative ways of solving a given problem. Which one to use is left at the reader's discretion.
The reason we don't use case expressions all the time is much like the reason we don't abuse
18
42
5. Recursion
Primitive recursion is the goto of functional programming.
(anonymous)
-- File : factorial . hs factorial :: Integral a => a -> a factorial 0 = 1 factorial n = n * factorial ( n - 1) ghci > factorial 3 6 ghci > factorial 5 120
It works, 1. 2. 3. 4. 5. 6. 7. 8. 9.
1 2 3 4
but why ?
factorial 4.
is is is
factorial 4
4 * (3 * (2 * (1 * 1))).
1 2
Author's note: it took all my willpower not to start with a recursion joke . The factorial of a (non-negative) integer
n.
43
5. Recursion
10. Done! At this point, it's useful to make our line-by-line analysis. Here's the function again, without that pesky rst line comment: 1 2 3
Actually, it
factorial 1
would be
This is called the base case or edge condition. We'll discuss it in a moment. 3. This one puts an operation on hold (namely multiplication), then brings the evaluation closer to the base case. Eventually it will reach it, the pending operations will be performed, and the computation will end, as seen in the elaboration above. Sounds complicated? Because it is. The above operations aren't meant to be our concern. The compiler can do them without our help. We should understand recursion intuitively, and to do that, we must think simpler. Here's a little something to break the wall of text, and then we'll move on. 1 2 3 4 5
________ _____ ___ __ \ _____ ___________ _____________________ (_) ______ _______ __ /_/ /_ _ \_ ___ /_ / / / __ ___ / __ ___ / __ / _ __ \ __ __ \ _ _ , _ / / __ // / __ / /_/ / _ / _ ( __ ) _ / / /_/ /_ / / / /_ / |_ | \ ___ / \ ___ / \__ ,_/ /_ / / ____ / / _/ \ ____ / /_/ /_/
The bottom line is, a recursive function has two main elements: 1. The base case the simplest one, where we already know the answer. The base case is where the calculation ends. Some examples: a) The factorial of 0 is 1. We know this because it's convention. Can it get any simpler? Not really. b) The length of an empty list is 0. We know that because it's obvious. c) The maximum of a single number is that number. 2. All other cases here we must bring evaluation closer to the base case. must get closer. Some examples: a) The factorial of We must simplify. Why?
Because the base case is the only way our calculation can nish. We must reach it. To reach it, we
is
n 1; n 1
enough times, we'll reach the empty list, as planned. c) The maximum of a list is the rst element or whichever is larger. In all three situations, the regular cases bring us closer to the edge condition (base case), thus guaranteeing that the computer will, in fact, nish calculating and provide a result.
44
5. Recursion
4 one.
-- File : length . hs length ' :: [ a] -> Int length ' [] = 0 -- what are we supposed to do now ?
Obviously, the list with the rst element is one longer then the list without it. We should somehow write this down, but to do it, we must separate the list into its rst element and the rest. Do we know something that does that? Yes, it's the
x:xs
pattern. We've already covered some of its uses, but here is a quick refresher:
1 2 3 1 2
-- File : xxs . hs super :: String -> String super ( x: xs ) = " First letter : " ++ [x] ++ "; the rest : " ++ xs ghci > super " Greetings ! " " First letter : G; the rest : reetings !"
Now we can state the obvious, clearly and concisely.
1 2 3 4 1 2 3 4
-- File : length . hs ( FIXED ) length ' :: Num a => [b] -> a length ' [] = 0 length ' (x: xs ) = 1 + length ' xs ghci > length ' [1 ,2 ,3 ,4] 4 ghci > length ' " haskell " 7
We might even notice that we're not using
(from the
x:xs),
so we can write
length' (_:xs).
To determine the maximum of a list, we have to, once again, separate the list into a head and a tail. This time we get to see the completed code directly. 1 2 3 4 5
maximum . hs :: Ord a => [a ] -> a [] = error " maximum ' of empty list " [x ] = x (x : xs ) = max x ( maximum ' xs ) []
two
the maximum of an empty list doesn't make sense. The other one is the normal base
case we all know and love the maximum of a single element is itself. The third pattern compares the head with the maximum of the tail to determine which one is bigger. Notice how 1 2
max
maximum'
length'
because
length
45
5. Recursion
replicate. replicate
1 2 3 4 5 6
ghci > :t replicate replicate :: Int -> a -> [ a] ghci > replicate 5 2 [2 ,2 ,2 ,2 ,2] ghci > replicate 6 'a ' " aaaaaa "
It's easier if we try to implement it for a certain element, say
'A'.
1 2 3 4 5
-- File : screamer . hs -- replicate when applied to the letter 'A ' screamer :: Int -> String screamer 0 = [] -- it ' s the same as "" screamer n = 'A ' : screamer (n -1)
Obviously,
replicate
1 2 3 4 1 2 3 4
-- File : replicate . hs replicate ' :: Int -> a -> [a ] replicate ' 0 _ = [] replicate ' n x = x : replicate ' (n -1) x ghci > replicate ' 3 'b ' " bbb " ghci > replicate ' 2 " Hi " [" Hi " ," Hi "]
This time, one of the parameters (namely, the second one) always remained unchanged. But it is not always so. We can manipulate several parameters when writing a recursive function. This very dumb implementation of
compare,
1 2 3 4 5 6
-- File : dumb - compare . hs cmp :: Integer -> Integer -> Ordering cmp 0 0 = EQ cmp 0 _ = LT cmp _ 0 = GT cmp x y = cmp (x -1) (y -1)
This example also illustrates a good rule of thumb : the number of base cases is usually equal to the number of possible outcomes. In this case, it's three:
EQ, LT
and
GT. compare?
Anyway, the principle of this function is very simple. It decrements The other is larger. Is there an even more inecient version of
both
take
1 2 3 4
takes taking elements from a list to a whole new level. Example, then code.
replicateA
46
5. Recursion
1 2 3 4
-- File : take . hs take ' 0 _ = [] take ' _ [] = [] take ' n (x : xs ) = x : take ' (n -1) xs
Notice how the two outcomes become base cases. We either
take 0 elements from a list, or try to take elements from an empty list.
[].
n-1
zip.
This function takes two lists and combines them together into a list of pairs. It stops when
is
[('a',1),('b',2)].
The two edge conditions correspond to empty lists (the rst and the second, respectively). The general case separates both lists in a head and a tail. 1 2 3 4 5
-- File : zip . hs zip ' :: [a] -> [b] -> [(a , b)] zip ' [] _ = [] -- First list empty zip ' _ [] = [] -- Second list empty zip ' (x : xs ) (y : ys ) = (x , y) : zip ' xs ys
5.2. Variations
5.2.1. Using Guards
If we're not careful, we might as well end up with a function that runs indenitely, or worse . This usually happens if the edge condition is poorly written, or if the general case does not lead to the edge condition. Half the functions we've written so far have some sort of problem. That's not very encouraging. Our version of
replicate (also, screamer) weirds out when we give it a negative number of repetitions.
The
ghci > replicate ' ( -2) 5 [5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,^ CInterrupted . ghci > replicate ( -2) 5 []
Warning! Make sure your function behaves correctly even on unexpected input.
The problem? Our edge condition should also check for negative numbers. The easy way to do it is to use a guard.
1 2 3 4
-- File : replicate . hs ( FIXED ) replicate ' :: Int -> a -> [a ] replicate ' n _ | n <= 0 = [] replicate ' n x = x : replicate ' (n -1) x
This is one of the few acceptable uses of inline guards. Notice the absence of an catches everything). In this instance, we can also use
otherwise
clause. This is
because if evaluation reaches the end of the guards, it will fall down to the next pattern (which, in our case,
otherwise
47
5. Recursion
1 2 3 4 5
-- File : replicate2 . hs replicate ' :: Int -> a -> [a ] replicate ' n x | n <= 0 = [] | otherwise = x : replicate ' (n -1) x
Sometimes the function does something unimaginable. Our stupid The relevant parts, then illustration:
1 2 3 4
0 0 _ x
0 _ 0 y
= = = =
1 2 3 4 5 6
ghci > cmp 2 3 LT ghci > cmp ( -2) 3 GT ghci > cmp ( -2) ( -3) ^ CInterrupted .
Why does this happen? The program assumes that the rst number to reach 0 is smaller. But if we decrease an already negative number, it will never become 0. So the other one will be 0 rst, and will be declared the smallest. If both are negative, then the function will continue to run, and run, and run (until we run out of memory) . Here is the corrected function:
1 2 3 4 5 6
-- File : dumb - compare . hs ( FIXED ) cmp :: Ord a = > a -> a -> Ordering cmp x y | x == y = EQ | x <= y = LT | otherwise = GT
The dumb implementation is doomed. There is no way we can get something usable out of it, so we should just trash it.
If it's even, divide it by two. If it's odd, multiply it by three and add one.
It is thought (but not proven) that after a nite number of steps, all numbers will eventually reach 1. By virtue of this fact, we know our edge condition. The two regular cases are for even and odd, respectively.
Integer
48
5. Recursion
1 2 3 4 5 6
-- File : collatz . hs collatz :: Integral a = > a -> [ a] collatz 1 = [1] collatz n | even n = n : collatz (n `div ` 2) | otherwise = n : collatz (3* n + 1)
This function is especially dangerous because we don't actually know if it will nish. Still, let's take it for a spin.
1 2 3 4
ghci > collatz 5 [5 ,16 ,8 ,4 ,2 ,1] ghci > collatz 20 [20 ,10 ,5 ,16 ,8 ,4 ,2 ,1]
Of course, we can simply check the lengths. Some inputs are especially pesky .
1 2 3 4
ghci > length ( collatz 27) 112 ghci > length ( collatz 6171) 262
repeat cycle
1 2 3 4 5 6
The easy way to do it is to simply omit the edge condition, like this:
-- File : inf - recursion . hs repeat ' :: a -> [ a] repeat ' x = x : repeat ' x cycle ' :: [a] -> [ a] cycle ' xs = xs ++ cycle ' xs
Without a base case, the function is all but guaranteed to run indenitely. That is, unless we number of elements (because of laziness).
take
a nite
1 2 3 4
ghci > take 5 ( repeat ' 0) [0 ,0 ,0 ,0 ,0] ghci > take 10 ( cycle ' [1 , 2, 3]) [1 ,2 ,3 ,1 ,2 ,3 ,1 ,2 ,3 ,1]
The Online Encyclopedia of Integer Sequences has collected a list specially for the purpose: A006877.
49
5. Recursion
1 2 3
1 2 3 4
:: Integral a => a -> a n | n < 0 = error " factorial over negative numbers " 0 = 1 n = n * factorial ( n - 1)
That's more of a workaround rather than a x, however. Someone casually looking at the type denition might imagine that the function works over all integers. This is obviously not the case. The right way to do it is to use the appropriate type for the function; something like natural numbers would be welcome. libraries. [FIXME] 1 2 3
Nat
representing
quicksort.
10 the list with
What it does: it sorts a list (duh). How it does it: a sorted list is
the elements less than or equal to the head, the head of the list, followed by the elements greater than the head,
sorted, followed by
sorted.
quicksort
twice in its denition (once for the smaller
What's interesting for us is that we must call elements and once for the larger ones)
-- File : quicksort . hs quicksort :: Ord a => [a] -> [ a] quicksort [] = [] quicksort (x: xs ) = lesserSorted ++ [x] ++ greaterSorted where lesserSorted = quicksort [ y | y <- xs , y <= x ] greaterSorted = quicksort [ y | y <- xs , y > x ] ghci > quicksort [4 ,1 ,5 ,3 ,8 ,7] [1 ,3 ,4 ,5 ,7 ,8] ghci > quicksort " the five boxing wizards jump quickly " " abcdeefghiiiijklmnopqrstuuvwxyz "
10
We can't say it does this, then it does that, because it defeats the purpose of functional programming, which emphasizes how things are dened, rather then how they are done.
1 2 3 4
50
5. Recursion
This implementation of quicksort is surprisingly easy to understand. The function will take the head of the list,
[1,3]
and
[5,8,7]
Such an algorithm is called divide and conquer because it literally The pieces are then put back together in the correct order.
manage halves, each of them broken down even more, until we reach empty lists, which are already sorted.
Unfortunately, if we perform the detailed breakdown on this function, we clearly see that the algorithm performs many useless operations (concatenating all those empty lists), so it might not be terribly ecient. [FIXME-double check] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-- Evaluation steps quicksort [4 ,1 ,5 ,3 ,8 ,7] = quicksort [1 ,3] ++ [4] ++ quicksort [5 ,8 ,7] quicksort [1 ,3] = quicksort [] ++ [1] ++ quicksort [3] quicksort [] = [] quicksort [3] = quicksort [] ++ [3] ++ quicksort [] quicksort [] = [] quicksort [] = [] quicksort [5 ,8 ,7] = quicksort [] ++ [5] ++ quicksort [8 ,7] quicksort [] = [] quicksort [8 ,7] = quicksort [7] ++ [8] ++ quicksort [] quicksort [7] = quicksort [] ++ [7] ++ quicksort [] quicksort [] = [] quicksort [] = [] quicksort [] = [] [] ++ [1] ++ [] ++ [3] ++ [] ++ [4] ++ [] ++ [5] ++ [] ++ [7] ++ [] ++ [8] ++ [] [1 ,3 ,4 ,5 ,7 ,8]
Indeed, running more
Data.List
quicksort on [100000,99999..1] takes quite some time and maxes out the memory. From Data.List, which conveniently contains an ecient sorting function, sort12 . For
goodies, see C.1.
1 2 3
ghci > import Data . List ghci > sort [3 ,5 ,8 ,2 ,1] [1 ,2 ,3 ,5 ,8]
5.3.3. Discussion
All of the functions that we have implemented in this chapter have some common ground. For instance:
[].
Having some number and then decreasing it until it becomes 0. Breaking down a list into several smaller parts.
By far the most widely used data structure in this chapter was the list. Somehow lists lend themselves to being recursed upon simply because of the convenient
which can be used and, on the other hand, leaves the rest of the list available for further operations. One of the main development directions in Haskell is abstraction. Sadly, in this book, this path has been so far left unexplored (because we were busy understanding syntax). this is an implementation of Specically, the primitive (explicit) recursion we have performed so far in this chapter allows us to consider only particular cases. For instance,
sum:
11 12
51
5. Recursion
1 2
product:
1 2
and
True.
Here it is:
1 2
or:
1 2
or [] = False or (x: xs ) = x || or xs
A pattern emerges. All these concrete examples have the same basic structure,
There must be a function that covers all these use cases. There is.
52
6. Advanced Functions
I've come to see the power of Haskell at last. You have to treat functions like crap.
(nikki93)
currying
Problem Z
a function
compare 2 3
that takes a parameter and compares 2 with it. Read that again. type, it's
LT.
That function
compare's
a -> a -> Ordering is the same as a -> (a -> Ordering). So the function, in fact, takes only one parameter (an a) and returns an a -> Ordering, which is a function (that takes an a and returns an Ordering).
But now we realize that Let's discuss a clearer example. 1 2 3 4
-- File : currying . hs addFour :: Int -> Int -> Int -> Int -> Int -- we can also write Int -> ( Int -> ( Int -> ( Int -> Int ))) addFour x y z t = x + y + z + t
Now if we add parameters one at a time:
1 2 3 4 5 6 7 8 9 10
ghci > :t addFour addFour :: Int -> Int -> Int -> Int -> Int ghci > :t addFour 1 addFour 1 :: Int -> Int -> Int -> Int ghci > :t addFour 1 2 addFour 1 2 :: Int -> Int -> Int ghci > :t addFour 1 2 3 addFour 1 2 3 :: Int -> Int ghci > :t addFour 1 2 3 4 addFour 1 2 3 4 :: Int
53
6. Advanced Functions
Every time we add another parameter, the type gets eaten up from the left. That is because if we call
application.
f a1
a function with too few parameters, we'll get a function that takes the rest of them. This is called In other words, if
takes
...,
an,
partial
then:
takes
n1
parameters:
f a1 a2
etc.
takes
n2
parameters:
This is also the chief reason why everything is separated by wouldn't be able to do other neat things, like name them. 1 2 3 4 5 6 7
the parameters from the return type, we couldn't have parially applied functions and thus, indirectly, we
Of course,
We've
done things this way many times before. I know we're repeating ourselves, but let's see them again. 1 2
compare2With x = compare 2 x -- the way we ' ve done things compare2With = compare 2 -- equivalent to the above
Notice how
(it can safely be removed). Watch out, though, because in something like (x on the left), can't be eliminated without changing the meaning.
Warning! Partial application only occurs from left to right (beginning with the rst parameter).
So there you have it. Currying is often confused with partial application, but they are really quite dierent:
Currying is what makes a function take only one parameter and return a function that takes another parameter and so on. We'll discuss it a little later, in [XREF]. Partial application is the act of supplying a function with too few arguments.
Currying and partial application are two of the most important concepts in all of Haskell, so it's a good idea to be familiar with them.
6.1.2. Problem Z
We've put all the cool things that happen because of currying and partial application under the umbrella term
In 2.1.3 we said that a constant really is a zero-parameter function. It makes sense if we think about it there are no parameters for us to change so the result will always be the same. Do we know what else takes zero parameters? A fully-applied function. Take 1 2 3 4 5 6
compare 2 3
for instance.
ghci > :t compare 2 3 compare 2 3 :: Ordering ghci > :t LT LT :: Ordering ghci > LT == compare 2 3 True
1
We're going to say that a function takes
parameters for simplicity, even though we know what's actually going on.
54
6. Advanced Functions
Moving on, when we discussed inx functions (in 2.1.4) we illustrated how inx functions can be called prex. 1 2 3 4
1 2
sections.
1 2 3 4
ghci > :t (2/) (2/) :: Fractional a => a -> a ghci > :t (/2) (/2) :: Fractional a => a -> a
We still have to put them in parentheses because otherwise the compiler will treat them as incomplete expressions. Sections have another advantage. Notice the dierence between the following two:
1 2 3 4
second
(3,) 2,
ghci > (3 ,) 2 < interactive >:1:1: Illegal tuple section : use - XTupleSections
What GHCi means by this is that it recognizes what we're trying to do, but won't allow it. It also mentions that if we open GHCi with the option
-XTupleSections,
1 2 3 4 5 6
ee@bt :~ $ ghci - XTupleSections GHCi , version 7.4.1: http :// www . haskell . org / ghc / Loading package base ... linking ... done . Prelude > : set prompt " ghci > " ghci > (3 ,) 2 (3 ,2)
But why bother when we can just use
:? for help
(,)
instead?
1 2 3 4
55
6. Advanced Functions
(a -> a)
thing is a
single parameter: a function that takes something of a type and returns something of the same type. We
->
as
f x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
f2
takes a function,
f,
and a value,
f2
x.
to
(the
f 2 (x) = f (f (x)).
ghci > succ 3 -- successor 4 ghci > succ 4 5 ghci > succ ( succ 3) 5 ghci > f2 succ 3 5 ghci > f2 pred 3 -- predecessor 1 ghci > f2 sqrt 16 2.0 ghci > f2 tail " abcd " " cd " ghci > f2 head " abcd " -- whoops , we need the function to return the same type < interactive >:22:4: Couldn 't match type `Char ' with `[ Char ] ' Expected type : [ Char ] -> [ Char ] Actual type : [ Char ] -> Char In the first argument of `f2 ', namely `head ' In the expression : f2 head " abcd "
We now understand better how f2 works and we know why (a -> a). If our function takes an Int and returns a Bool, resulting Bool it's the wrong type. While we can call the function we pass has to have the type there's no way we can call it again on the
head
[[2,3][4,5]]
(it returns
2),
using
f2
Moreover, there's no easy way to modify it so it can work. We'll discuss this in [XREF], as well as provide an adequate solution. Very few functions take a single parameter and return something of the same type. We can, however, partially apply functions to the point of accepting only one parameter, and then pass them to useful partial application becomes in this case.
f2.
We know that functions really only take a single parameter at a time. But it would save us some time and eort to think of them as taking several parameters.
56
6. Advanced Functions
1 2 3 4 5 6 7 8 9 10 11 12
ghci > 13 ghci > 100 ghci > 81 ghci > " aab " ghci > " baa " ghci > " aab "
f2 (+ 2) 9 f2 (* 5) 4 f2 (^2) 3 f2 ( "a" ++) "b" f2 (++ " a") "b" f2 ( 'a ' :) " b"
f2
(of type
a -> a)
to
(a value of type
a)
n sex.
drink :: ( Fractional a , Ord a ) = > String -> a -> String drink sex n | bac < 0.03 = " You ' re as sober as can be expected ." | bac < 0.08 = " You can drive , but it 's a bad idea ." | bac < 0.10 = " Your reasoning is out the window . " | otherwise = " Stop drinking . " where bac = n * if sex == " male " then 0.025 else 0.035
We can dene an additional function like below, but since we're talking about higher-order functions, there is another way.
1 2
flipDrink :: ( Fractional a , Ord a) => a -> String -> String flipDrink n sex = drink sex n
In this case, we shall use
flip. flip is a nice built-in function that reverses the parameters of a two-parameter
4
Quick reminder: despite what the syntax highlighter may imply [FIXME-I'm working on it, but it seems to be a particularly thorny problem], the quote doesn't do anything. It's just another character in the function name so that with the predened
flip.
57
6. Advanced Functions
1 2
flip'
will call them in the right order (what the compiler wants), just like
flipDrink
above.
Some examples: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
ghci > drink " female " 2 " You can drive , but it 's a bad idea . " ghci > flip ' drink 2 " female " " You can drive , but it 's a bad idea . " ghci > ( -) 3 2 1 ghci > flip ' ( -) 2 3 1 ghci > (++) " hello " " world " " helloworld " ghci > flip ' (++) " hello " " world " " worldhello " ghci > zip [1 ,2 ,3] [4 ,5 ,6] [(1 ,4) ,(2 ,5) ,(3 ,6) ] ghci > flip ' zip [1 ,2 ,3] [4 ,5 ,6] [(4 ,1) ,(5 ,2) ,(6 ,3) ] ghci > flip ' ( zip [1 ,2 ,3] [4 ,5 ,6]) -- nope , error
What happens if we partially apply parameters reversed.
flip'?
1 2 3 4 5 6 7
ghci > :t zip zip :: [a ] -> [b] -> [(a , b) ] ghci > :t flip ' zip flip ' zip :: [ b] -> [a ] -> [(a , b )] ghci > let oddDivision = flip (/) ghci > 2 ` oddDivision ` 3 1.5
If we give it parameter:
a function
that function
on its second
1 2 3 4 5 6
map
and
zipWith
map,
which we'll call
Another cool (and useful) thing we can do is apply a function to every element in a list using before, we can have our own
map'.
map.
Like
58
6. Advanced Functions
1 2 3
map ' :: (a -> b ) -> [a] -> [b] map ' _ [] = [] map ' f ( x: xs ) = f x : map ' f xs
This is the rst time we use higher order functions and recursion simultaneously. First, as always, the type declaration:
map'
b)
and a
Recall how we learned them during the recursion chapter. function (any function, thus the
_)
The third line: mapping a function list with the rst element
f x
over
xs.
xs
is a
In other words,
we apply the function element by element, starting with the rst one. Example: 1 2
map' succ [6,9,3] map' succ [9,3] map' succ [3] map' succ []
is is is
is
which is
which is
[],
7 : 10 : 4 : [],
We're gonna assume that we've gained a sucient understanding of recursion such that elaborations like the one above aren't necessary from now on. [FIXME] NOTE: if I haven't explained things well enough and by this point you do not where you got lost so I know where to improve. I'd really appreciate it. Thanks! Some more examples with 1 2 3 4 5 6 7 8 9 10 11 12 13 14
fully
understand
map',
ghci > map ' pred [6 ,9 ,3] [5 ,8 ,2] ghci > map ' sqrt [4 ,9 ,16] [2.0 ,3.0 ,4.0] ghci > map ' (+2) [10 ,20 ,30 ,40] [12 ,22 ,32 ,42] ghci > map ' (==5) [2 ,5 ,3 ,5] [ False , True , False , True ] ghci > map ' (4/) [4 ,2 ,1 ,0.5] [1.0 ,2.0 ,4.0 ,8.0] ghci > map ' (++ " aa ") [" bb " , " cc "] [" bbaa " ," ccaa " ] ghci > map ' ( 'x ':) [" b" , "a" , "r "] [" xb " ," xa " ," xr "]
Another function,
zipWith,
is just like
map,
but it operates on
two
:: (a -> _ [] _ = _ _ [] = f ( x: xs )
map'
Shorter explanation:
and returns a
b)
and a list of
as
bs.
59
6. Advanced Functions
map
it is.
So,
zipWith
ghci > zipWith ' (+) [2 ,3 ,4] [5 ,6 ,7] [7 ,9 ,11] ghci > zipWith ' (++) [" hello " ," bye " ] [" world " ," everyone "] [" hello world " ," bye everyone "] ghci > zipWith ' (*) [1..6] [2 ,2..] [2 ,4 ,6 ,8 ,10 ,12] ghci > zipWith ' compare [5 ,6 ,7] [3 ,10 ,7] [GT ,LT , EQ ] ghci > zipWith ' (&&) [ True , True ] [ True , False ] [ True , False ] ghci > zipWith ' (++) [" aa " , " bb "] [ " xx " , " yy " ] [" aaxx " ," bbyy " ]
Now we see another useful application of
flip6 .
ghci > zipWith ' ( flip (++) ) [" aa " , " bb "] [ " xx " , " yy " ] [" xxaa " ," yybb " ] ghci > flip ( zipWith ' (++) ) [" aa " , " bb "] [ " xx " , " yy " ] [" xxaa " ," yybb " ]
It's interesting how both methods work. The rst one passes a function with its parameters reversed. The second ips the lists around. The end result is the same, but we usually use the rst one as it's more readable. Remember the
1 2 3 4
ghci > zip [1 ,2 ,3] " abc " [(1 , 'a ') ,(2 , 'b ') ,(3 , 'c ') ] ghci > zipWith ( ,) [1 ,2 ,3] " abc " [(1 , 'a ') ,(2 , 'b ') ,(3 , 'c ') ]
Additionally, we can continue with the lists. There actually is such a function,
1 2 3 4 5
:: ( a -> _ [] _ _ _ _ [] _ _ _ _ [] f (x : xs )
It's fairly easy to create such functions for 4, 5 or even more lists, but extremely dicult to make one to work for an arbitrary number of them. We'll look into this much later on, in [XREF].
and
isInfinite
(notice how some of them are partially applied functions). They can be used as such, like below, or can be passed to a higher-order function.
6 7
(,)
60
6. Advanced Functions
1 2 3 4 5 6 7 8 9 10 11 12 13 14
ghci > False ghci > True ghci > False ghci > True ghci > True ghci > False ghci > True
null [2 ,3] ( >3) 6 even 5 (==2) 2 or [ True , True , False ] ( elem 'a ') " hello world " isInfinite (1/0)
Using them as parameters for other functions can be extremely useful, but rst we need to know a couple of functions that accept predicates.
filter
filter :: (a -> Bool ) -> [a] -> [a] filter _ [] = [] filter p (x: xs ) = if p x then x : filter p xs else filter p xs
We immediately notice the predicate: it's the rst parameter, of type list, element by element, keeping those that satisfy the predicate then include else exclude).
p8
a -> Bool.
p x
1 2 3 4 5 6 7 8 9 10
ghci > filter [4 ,2 ,6 ,8 ,2] ghci > filter [4 ,5] ghci > filter [4 ,6 ,7] ghci > filter [" abstract " ] ghci > filter [[] ,[]]
even [5 ,4 ,2 ,1 ,3 ,6 ,8 ,2] ( >3) [4 ,3 ,2 ,1 ,5 ,0] (/= 5) [4 ,5 ,6 ,7] ( elem 'a ') [" hello " , " abstract " , " gemini " ] null [[5 ,6] ,[7] ,[] ,[8 ,9] ,[]]
filter
quicksort.
:: Ord a => [a] -> [ a] [] = [] (x: xs ) = lesserSorted ++ [x] ++ greaterSorted lesserSorted = quicksort ( filter ( <= x) xs ) greaterSorted = quicksort ( filter (> x) xs )
We've recycled the example from 5.3.2, but instead of using list comprehensions, we used lters. In fact, more of the stu we've discussed so far (like about this in 6.3.3. Before we discuss applications, let's look at two functions which are very similar to
map)
dropWhile.
8
While we call our functions
filter: takeWhile
and
f, g
and
q.
61
6. Advanced Functions
takeWhile takes a predicate and a list. Like filter, it takes elements which satisfy Unlike filter, it stops entirely when it encounters an element that doesn't satisfy. dropWhile
1 2 3 4 5 6 7 8 9 10 11 12 is similar to
the predicate.
takeWhile
but it returns the rest of the list, starting with the rst element
ghci > filter ( >3) [4 ,6 ,2 ,1 ,8 ,7] [4 ,6 ,8 ,7] ghci > takeWhile ( >3) [4 ,6 ,2 ,1 ,8 ,7] [4 ,6] ghci > dropWhile ( >3) [4 ,6 ,2 ,1 ,8 ,7] [2 ,1 ,8 ,7] ghci > filter (/= ' ') " hello dear world " " hellodearworld " ghci > takeWhile (/= ' ') " hello dear world " " hello " ghci > dropWhile (/= ' ') " hello dear world " " dear world "
We'll let the source code speak for itself (this time we're using guards instead of explicit if..else, and we're showing another indentation style ):
1 2 3 4 5 6 7 8 9 10 11
takeWhile takeWhile _ [] takeWhile p (x : xs ) | p x | otherwise dropWhile dropWhile _ [] dropWhile p xs@ (x :xs ') | p x | otherwise
Recall the as patterns (4.1.4):
name
is
xs
and
pattern
is
name@pattern (x:xs').
pattern
by using
name;
in our case,
map
and
filter,
map (+2) xs *.
is
[ 2*x | x <- xs, even x, x >= 2 ] map (2*) (filter even (filter (>=2) xs)), but is much more concise than [ x + 2 | x <- xs ].
is creating a list of functions by passing a two- (or
One extremely cool thing that can be done with more-) parameter function, such as
map
This means that the resulting list will contain partially applied functions: elements from it and fully apply them: [FIXME-elaborate on this]
(5*), (4*)
These examples are identical to those in the ocial source code. It's not a coincidence; that's where I took them from.
62
6. Advanced Functions
1 2 3 4 5
ghci > let functions = map (*) [5 ,4 ,3 ,2 ,6] ghci > :t functions functions :: [ Integer -> Integer ] ghci > ( head functions ) 8 40
We can totally do it with list comprehensions, as well:
1 2 3
!! 5
!!
0.
So while
6 is the fth
!! 4.
Performing
error.
takeWhile
dropWhile
don't have an easy list comprehension equivalent, so we won't talk about them
zipWith
10, 1
with
10, 2 with 20 and 3 with 30), the list 20, 1 with 30, 2 with 10 and so on).
ghci > zipWith (+) [1 ,2 ,3] [10 ,20 ,30] [11 ,22 ,33] ghci > [ x + y | x <- [1 ,2 ,3] , y <- [10 ,20 ,30] ] [11 ,21 ,31 ,12 ,22 ,32 ,13 ,23 ,33]
It's a fundamental dierence, but also one easily overlooked.
zipWith
let or a where.
rather not name it at all? Introducing anonymous functions, or lambdas for short. What better way to show
\10 ,
and instead of
we write
->.
Additionally, by using lambdas, we not only specify the function, but we also call it. This is a really nice timesaver, because we usually create anonymous functions to pass them to higher-order functions, where they will be called anyway
10 11
63
6. Advanced Functions
1 2 3 4 5
1 2 3 4 5 6
ghci > map (\ x -> 2* x + 3) [1..5] [5 ,7 ,9 ,11 ,13] ghci > filter (\ x -> x ^2 > 16) [10 ,20 ,5 ,4 ,1 ,6] [10 ,20 ,5 ,6] ghci > zipWith (\ x y -> x + 2* y) [1 ,2 ,3] [4 ,5 ,6] [9 ,12 ,15]
Don't become overzealous with lambdas, though. We might be tempted to use them when it's not necessary:
1 2 3 4
ghci > map (\ x -> x + 2) [1 ,2 ,3] [3 ,4 ,5] ghci > map (\ x -> sqrt x) [4 ,9 ,25] [2.0 ,3.0 ,5.0]
Here, we're better o using the functions directly:
1 2 3 4
ghci > map (+2) [1 ,2 ,3] [3 ,4 ,5] ghci > map sqrt [4 ,9 ,25] [2.0 ,3.0 ,5.0]
One great thing about anonymous functions is that, like regular (named) functions, we can use pattern matching in them. Unlike regular functions, though, we have only one body so we can use only one pattern. If that fails, crash!
1 2 3 4 5 6 7 8
ghci > map (\( x ,y) -> compare x y ) [(3 ,4) , (5 ,6) , (7 ,7) , (9 ,8) ] [LT ,LT ,EQ , GT ] ghci > map (\( x: xs ) -> (x , xs )) [[2 ,3 ,4] , [8 ,10 ,20]] [(2 ,[3 ,4]) ,(8 ,[10 ,20]) ] ghci > map (\( 'a ': xs ) -> xs ) [" animal " , " anonymous " ] [" nimal " ," nonymous "] ghci > map (\(3: xs ) -> xs ) [[4 ,5]] [*** Exception : < interactive >:74:6 -18: Non - exhaustive patterns in lambda
One nal cool thing before we nish with lambdas: because of currying (and the fact that lambdas extend all the way to the right if we don't put them in parentheses), the following two are equivalent:
=:
64
6. Advanced Functions
3 4 5 6 7 8
sum [] = 0 sum ( x: xs ) = x + sum xs product [] = 1 product (x: xs ) = x * product xs and [] = True and ( x: xs ) = x && and xs or [] = False or (x: xs ) = x || or xs
The common pattern is:
1 2
12 :
1 2
someFunction, which takes two parameters and returns a third. *, && etc. startingValue, xs,
which can be
+,
2. 3.
0, 1, True
eat,
because it kinda looks like we're eating the list. Okey dokey, here we go. The
eat :: (a -> a -> a) -> a -> [a] -> a, but maybe making
for conciseness. The edge condition is easy eating the
everything the same type is too specic. Let's skip this one and let GHCi infer the type when we're nished. Let's call
someFunction f
and
startingValue x0
12
Refresher: A prex function comes before its parameters: are equivalent. Notice the backquotes.
f x y.
x `f` y.
The notations
65
6. Advanced Functions
eat f x0 [] = x0
We know that we have to split the list into a head and a tail.
1 2
eat f x0 [] = x0 eat f x0 ( x: xs ) =
Looking back at
ourFunction,
to
eat f x0 [] = x0 eat f x0 ( x: xs ) = f x
Now, what is call
eat
with
f's second parameter? The whole thing should be recursive, xs. We shouldn't forget the parentheses to group eat xs.
1 2
1 2
eat.hs)
1 2 3
It compiled! This usually means we did it right. Let's check its type. 1 2
eat :: (a -> a -> a) -> a -> [a] -> a guess was indeed too specic. But it was close! Let's write the type declaration (using a and b instead of the ugly t and t1), align things a little and marvel
at our handiwork:
1 2 3 4
-- File : eat . hs eat :: (a -> b -> b) -> b -> [ a] -> b eat f x0 [] = x0 eat f x0 ( x: xs ) = f x ( eat f x0 xs )
Now let's go ahead and try to dene
0.
1
sum
in terms of
eat.
+.
Let's go!
xs
is redundant
sum'
has to be included in
eat.hs
13
66
6. Advanced Functions
1 2 3 4
1 2 3
product ' = eat (*) 1 and ' = eat (&&) True or ' = eat (||) False
Let's test them as well.
1 2 3 4 5 6 7 8
ghci > 120 ghci > 0 ghci > False ghci > True
This
product ' [4 ,5 ,6] product ' [0] and ' [ True , True , False ] or ' [ False , True , False ]
eat
function is really useful. How come it's not predened? Let's do a Hoogle search for its type.
foldr.
ghci > let sum ' = foldr (+) 0 ghci > sum ' [1 ,2 ,3 ,4 ,5] 15
This is actually a big problem when writing Haskell programs. Because most functions are so abstract,
there's almost always one that covers our particular need in this case, Our time was not lost, however, as we now know how
foldr. eat,
was not so inspired.
foldr
good idea on how to design and test new functions. Too bad our function name,
to understand how to make the most of it. worry, we rarely use them all. [FIXME]
eat, which turned out to be our own implementation of foldr, it's time We should mention that there are six dierent folds: foldr, foldr', foldr1, foldl, foldl', and foldl1. That's a lot of functions that do the same thing14 ! Don't
14
You may be wondering why we need six dierent functions for this. I'll be happy to point out in Lisp.
and
67
Part III.
Appendices
68
A. Miscellaneous
A.1. Functions
A.1.1. Fixity
The following table in Prelude. Precedence 9 8 7 6 5 4 3 2 1 0 Left-associative Non-associative Right-associative
1 shows the precedence and xity (left-, non-, and right- associativity) of the operators
!! *, /, `div`, `mod`, `rem`, `quot` +, ==, /=, <, <=, >, >=, `elem`, `notElem` >>, >>=
. ^, ^^, **
Below are some examples of precedence and xity declarations (if an operator denition lacks a xity declaration it is assumed to be 1 2 3 4 5 6 7 8 9
infixl 9).
-- File : fixity . hs x ++++ y = x + y + x *y infixl 3 ++++ -- left - associative x -.- y = x ^3 + y ^3 infixr 5 -.- -- right - associative func a b = a + b + b infix 2 `func ` -- non - associative
In many cases the correct xity declaration carries a great deal of importance let's take declared above) as an example.
-.-
(the one
1 2 3 4
-.- is right-associative in Haskell-speak, (a -.- b) -.- c is not the same as a -.- (b -.- c).
1
Taken from the Haskell 98 Report
69
A. Miscellaneous
tricky on many levels, mainly because laziness introduces important dierences between supercially similar
&&.
As a reminder,
&&
is
(&&) :: Bool -> Bool -> Bool True && x = x False && _ = False
Note how but
lazy
&&
&&
False: &&
is
must always evaluate the rst argument, but not necessarily the
2 (with instructions on how to evaluate them) . Let's take the following piece of code as an example:
1 2 3 4 5
thunks.
-- File : thunks . hs a (b , c) 1: d
Line-by-line: 1. Haskell matches
(length "hello", [1, 2, 3, 4]) to a. Because we do nothing to a, Haskell doesn't a is just a thunk.
In order to make sure the match succeeds and to assign the
a c,
necessary variables, 3.
(thunk, thunk). b
and
become thunks.
1:d. c
now becomes
1:thunk.
("hi", [4,
-- Evaluation steps thunk -- unevaluated ( thunk , thunk ) ( 'h ': thunk , thunk ) ( 'h ': 'i ': thunk , thunk ) ( 'h ': 'i ':[] , thunk ) ( 'h ': 'i ':[] , 4: thunk ) ( 'h ': 'i ':[] , 4:5: thunk ) ( 'h ': 'i ' :[] , 4:5:[])
normal form.
undefined.
error. 1 2
We don't always know which functions are strict and which are lazy, but we can check by calling them with If
undefined
70
A. Miscellaneous
3 4
&&
04 .
So we can put
undefined,
0 * x
x.
After all,
multiplied
can't we?
0.
What isn't surprising is that laziness is a touchy subject the best way to learn it is through experience.
f x y | g x y < 5 = " Less than 5" | g x y == 5 = " Equal to 5" | otherwise = " Greater than 5 " where g x y = 2* x + 3* y
The names (variables) 1. 2. 3. 4. 5.
and
f x y:
the parameters in
f's
g x y < 5:
g. =)
g x y == 5: 2*x + 3*y:
g x y = ...:
g's
the parameters in
g's
=)
Pertaining to Pertaining to
f: g:
1, 2, and 3 4 and 5
would be
called local variables because they are logically dierent and the dierence occurs only in a limited area:
g x y = 2*x + 3*y.
We can reect the dierence in meaning ourselves, by renaming them: 1 2 3 4 5
f x y | g x y < 5 = " Less than 5" | g x y == 5 = " Equal to 5" | otherwise = " Greater than 5 " where g a b = 2* a + 3* b
4
Not actually true:
is undened, and
0 (1)
is negative zero.
71
A. Miscellaneous
There are more examples of local variables, even in the interactive prompt: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
ghci > let ghci > let ghci > f x (2 ,3) ghci > f 4 (4 ,5) ghci > let (4 ,5) ghci > x 2 ghci > y 3 ghci > let ghci > f x (100 ,200)
x = 2; y = 3 f x y = (x ,y ) y 5 x = 4; y = 5 in f x y
x = 100; y = 200 y
let x = 2; y = 3 f x y f 4 5 x = 2
calls calls and
and
to be
and
3. (,)). 3.
are logically dierent than those from
let f x y = (x,y) f
and
y,
which are
and
f with 4 and 5. Because the x and y from (x,y) y = 3, the function behaves as expected.
5.
let x = 4; y = 5 in f x y temporarily binds 4 and 5 to x and y respectively, then calls the function
with those values.
6. 7. 8.
and
have not changed outside the previous expression they are still binds the new values of
and
3. y
100
and
200
to the names
and
let
GHCi and enter it again, the dened values are gone. What the
let
and then
100
and
200)
to
and
let
in 5 temporarily binds
and
to
and
until
f x y
previous values. What's going on here may be confusing, but hopefully it is somewhat intuitive. The point is that we're not talking of the same that: 1 2 3 4 5 6 7 8 9 10 11
and
xs
and
ys.
We can illustrate
ghci > ghci > ghci > (2 ,3) ghci > (4 ,5) ghci > (4 ,5) ghci > 2 ghci >
72
A. Miscellaneous
12 13 14 15
73
B.1.1.
Show
and
Read
Show and Read are handled Show and it does the rest.
by the computer
These two typeclasses are, for the most part, invisible to the user. Although almost every type out there belongs to both of them, hey, that type is part of
Show
contains all types which can be converted to strings. almost all types (Int,
Includes:
etc.)
functions (Int
none
Built-in functions:
converts a value to a string
ghci > show 5 "5 " ghci > show 203 " 203 " ghci > show False " False " ghci > show [1 , 2, 5] " [1 ,2 ,5] " ghci > show [" hi " , " hello " , " blah " ] -- result looks funky " [\" hi \" ,\" hello \" ,\" blah \"] "
Read
is the converse of
Show. shown.
Includes:
none
Built-in functions:
converts a string to a specic value .
1 2 3
Author's note: in retrospect, I don't know what I meant by saying this. They can also, however, be manually specied, but that's rare. The computer does a really good job. The type has to be specied, either by performing an operation and letting Haskell infer, or by explicitly declaring it. Otherwise, an ambiguous type variable error is thrown (details in B.2.2).
74
1 2 3 4 5 6 7
ghci > False ghci > True ghci > 156 ghci >
read " True " && False read " True " :: Bool read " 67 " + 89 read " 67 " -- ambiguous type variable error
B.1.2.
Many useful functions require membership in at least one of these typeclasses. After all, there is no function that can order unsortable items, and you can't list that which cannot be enumerated.
Eq
Includes:
none
Built-in functions:
tests for equality tests for inequality
ghci > 5 == 6 False ghci > " hello " == " hello " True ghci > (+) == (*) -- type error
Ord
contains types which have a logical ordering. almost all types functions
Includes:
>= <=
returns an ordering
* compare * max
1 2 3 4 5 6 7 8 9 and
min
ghci > False ghci > True ghci > False ghci > 10 ghci >
4 > 5 " abcd " >= " abcc " True < False max 10 3 compare 4 5
75
10 11 12 13 14
Includes:
ghci > 7 ghci > 'z ' ghci > '{ ' ghci >
succ 6 succ 'y ' succ 'z ' succ " abcde " -- type error
4 cannot be ordered5 .
Num
Includes: Int, Integer, Rational, Float, Double etc. Does not include: Built-in functions:
* +, -, * abs
and non-numbers
* negate * signum
1 2 3
The previous footnote was about complex numbers, not their ordering. Just a clarication.
on a positive number,
on zero, and
-1
on a negative number.
76
4 5 6 7 8 9 10
-10 ghci > abs ( -5) 5 ghci > signum 23 1 ghci > signum ( -23) -1
Integral
is the typeclass of integers.
Includes: Int, Integer and other size integers (Int8, Int16, Int32 etc.) Does not include: Built-in functions:
* quot, * div, * rem, * mod,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 the quontient in division integer division the remainder modulo function anything else
ghci > 5 ghci > 5 ghci > 2 ghci > 2 ghci > -5 ghci > -6 ghci > 2 ghci > -1
17 ` quot ` 3 17 `div ` 3 17 `rem ` 3 17 `mod ` 3 17 ` quot ` ( -3) 17 `div ` ( -3) 17 `rem ` ( -3) 17 `mod ` ( -3)
quot
with
div
and
rem
with
mod
Fractional
1 4
8.53)
Includes: Rational, Float, Double etc. Does not include: Prerequisites: Num Built-in functions:
* /,
the division function the inverse of a number ( , where integers, non-numbers
* recip,
7
1 x
is the number)
recip 0
gives
Infinity.
However,
Infinity
is not a number
in our calculations (which, by the way, is an extremely bad idea), we must use
1/0
or whatever.
77
1 2 3 4 5 6 7 8 9
ghci > 5.2 / 3.2 1.625 ghci > recip 0.25 4.0 ghci > 1 / 0.25 4.0 ghci > recip 0 Infinity ghci > 1 / Infinity -- doesn ' t work
Floating
contains decimal numbers
Prerequisites: Fractional
8
acos, asinh
etc.)
ghci > pi :: Float 3.1415927 ghci > pi :: Double 3.141592653589793 ghci > log 10 2.302585092994046 ghci > 5 ** 2.3 40.51641491731905 ghci > sin ( pi / 3) 0.8660254037844386 ghci > cos ( pi / 3) 0.5000000000000001 ghci > logBase 10 1000 2.9999999999999996
Warning! Watch out for rounding errors they're a pain in the brain.
It's a very interesting case because functions can be polymorphic and constants are (zero-parameter) functions, constants can also be polymorphic.
78
1 2 3 4 5 6 7 8 9
ghci > 1 * False < interactive >:1:1: No instance for ( Num Bool ) arising from the literal `1' Possible fix : add an instance declaration for ( Num Bool ) In the first argument of `(*) ', namely `1' In the expression : 1 * False In an equation for `it ': it = 1 * False
The analysis, as promised: 1. 2. 3. 4. 5. GHCi
ghci> 1 * False
<interactive>:1:1:
is the location in the program that gives the error ([line]:[character]). means that
False,
which is a
Bool,
Bools
6.
arising from the literal `1' tells us that it is through our use of 1, which is a number, inferred that False must also be a number so it can multiply them. But False is a Bool, and
aren't numbers.
Contradiction.
Possible fix: add an instance declaration for (Num Bool) suggests that it is possible to Bools can be numbers. For example, if we tell GHCi that False is the 9 same as 0 and True is really 1, then the expression would compile . Adding instance declarations is
x the error by dening how explained in [XREF].
7.
argument of 8. 9.
the rst
GHCi,
In an equation for `it': it = 1 * False gives the most general context it is an internal variable that stores the result of the previous computation.
the fastest way of identifying the problem, especially in very complex cases.
ghci > read "5 " < interactive >:1:1: Ambiguous type variable `a0 ' in the constraint : ( Read a0 ) arising from a use of `read ' Probable fix : add a type signature that fixes these type variable ( s) In the expression : read "5" In an equation for `it ': it = read " 5"
We shall, yet again, dissect the error. The line-by-line analysis shows that: 1.
9 10
In this case it's not recommended, seeing how multiplying a Other interpreters may display dierently.
Bool
79
2. 3.
is an empty line. GHCi has the tendency to put that before long errors.
<interactive>:1:1:
[line]:[character].
Here, it's at
5.
Read
contains multi-
ple types. What it doesn't say, but we know, is that Haskell must know the specic type to
can be
read
read.
For
as:
Probable fix: add a type signature that fixes these type variable(s) recommends xing the error by adding an expicit type signature would be the desireable action.
7. 8.
is the context of the ambiguity. With all this info, it's hard
:: Int -> [ Char ] 1 = " one " 2 = " two " 3 = " three "
error
1 2 3 4 5 6 7
-- File : patterns - wrong . hs ( FIXED ) intToString intToString intToString intToString intToString :: Int -> [ Char ] 1 = " one " 2 = " two " 3 = " three " _ = error " intToString : Number too large "
Notice that the error handler doesn't know the name of the function beforehand, so we might want to include it in the error message, like above. 1 2
:: Int
or
:: Char.
80
Because 1 2 3 4 5 6 7
error
-- File : patterns - wrong . hs ( FIXED ) intToString intToString intToString intToString intToString :: Int -> [ Char ] 1 = " one " 2 = " two " 3 = " three " n = error (" intToString : Number " ++ show n ++ " too large " )
1 2
graceful failure.
81
C. Modules
C.1. Data.List
The
Data.List
module is the one-stop shop for all our list goodies. It supports many functions, detailed
82