> module FridaySession1 where
> import Test.QuickCheck
Play, play, play
The quickest way to get fluent in Haskell is by using the interpreter as a tutoring tool.
Try out things all the time.
E.g. you are investigating map
and takeWhile
:
*FridaySession1> map (*2) [0..10]
[0,2,4,6,8,10,12,14,16,18,20]
*FridaySession1> takeWhile (\n -> even n || n < 10) [0..]
[0,1,2,3,4,5,6,7,8,9,10]
*FridaySession1> takeWhile (\n -> even n && n < 10) [0..]
[0]
*FridaySession1> takeWhile (\n -> even n && n < 10) (map (*2) [0..]
<interactive>:18:51:
parse error (possibly incorrect indentation or mismatched brackets)
*FridaySession1> takeWhile (\n -> even n && n < 10) (map (*2) [0..])
[0,2,4,6,8]
And so on
Using github and git
You can use the github interface and paste your homework directly into your repository.
But it is much more convenient to use git
.
The most important git commands are:
git clone <repository>
git pull
git add <file>
git commit -m "first homework"
git push
man gittutorial
Here is a guide to the tutorials
About stack
The simplest way to use stack is this:
stack exec ghci FridaySession1.lhs
For linux users it is convenient to add an alias to .bashrc
.
alias sghci="stack exec ghci"
Useful commands in GHCi
:q
exits.
:m Data.List
loads library module Data.List.
:r
reloads the input file.
:t
gives you the type of something.
:set +s
turns on timing and memory measurements.
With let x = 5
you can store something in memory.
This also works for functions:
Prelude> let x = 10
Prelude> x + 5
15
Prelude> let f = (\n -> n + 7)
Prelude> f 10
17
Prelude> f x
17
Using QuickCheck for system investigation
See this introduction to QuickCheck testing.
Example: find out the difference between mod
and rem
quickCheckResult (\ m n -> rem m n == mod m n)
This gives exceptions, because of a division by 0 error. Change this to:
quickCheckResult (\ m n -> n /= 0 ==> rem m n == mod m n)
This gives:
*** Failed! Falsifiable (after 9 tests and 5 shrinks):
-1
2
Now we see the difference:
*FridaySession1> rem (-1) 2
-1
*FridaySession1> mod (-1) 2
1
We looked at the difference between the following two functions:
> f1, f2 :: Int -> Int
> f1 n = sum [1..n]
> f2 n = n*(n+1) `div` 2
> qTest1 = quickCheckResult (\ n -> f1 n == f2 n)
This fails with a negative number counterexample. Now revise the test:
> qTest2 = quickCheckResult (\ n -> n >= 0 ==> f1 n == f2 n)
This succeeds.
We also looked at functions for converting lists to properties and vice versa. The Eq a
constraint on the type is crucial.
> list2prop :: Eq a => [a] -> a -> Bool
> list2prop = flip elem
To convert in the other direction we need a domain:
> prop2list :: [a] -> (a -> Bool) -> [a]
> prop2list domain p = filter p domain
But now that we see this definition, we realize that there is a simpler way:
> prop2list' = flip filter
Prefix vs. Infix
The + function is infix, i.e. we write 5 + 7
and not + 5 7
. To make an infix function a prefix function, we bracket it:
Prelude> (+) 5 7
12
Prelude> (*) 5 7
35
To turn a prefix function to an infix function, use ` `:
Prelude> 4 `elem` [1..]
True
Infix operators have to be written with non-alphabet symbols. To define them we actually define their prefix equivalent:
> (<-->) :: Int -> Int -> Int
> (<-->) m n = m + n + 7
FridaySession1> 4 <--> 10
21
Types and Type Classes
The function map
is fully polymorphic, here a
and b
can really be anything:
map :: (a -> b) -> [a] -> [b]
Similarly, addition can work on many things: Integers, Reals, Fraction, ... But there are some things which it will not work on: Strings, Boys, ... To distinguish these, Haskell uses type classes. In this case, the class is called Num:
(+) :: Num a => a -> a -> a
Even 5
and 23984724
are not of a fixed type but can be instantiated in every type that is in the type class Num.
5 :: Num a => a
23984724 :: Num a => a
(+23984724) :: Num a => a -> a
Another type class is Eq
. It contains all types for which we have an equality test. We need this for example in the definition of elem
:
elem :: Eq a => a -> [a] -> Bool
Redundant matching and brackets
Here are two tools to clean up your code:
hlint - complains about redundancy and ugly things It can be installed with "cabal install hlint"
the "-Wall" option for ghc and ghci This will complain about many things, for example missing type definitions and unused matches.
Example:
> bad_left_even :: (Int,Int) -> Bool
> bad_left_even (n,m) = (even n)
hlint says:
FridaySession1.lhs:144:3: Suggestion: Use camelCase
Found:
bad_left_even (n, m) = ...
Why not:
badLeftEven (n, m) = ...
FridaySession1.lhs:144:25: Suggestion: Redundant bracket
Found:
(even n)
Why not:
even n
2 hints
ghci -Wall says:
FridaySession1.lhs:143:20: Warning: Defined but not used: ‘m’
A better version is thus:
> leftEven :: (Int,Int) -> Bool
> leftEven (n,_) = even n
Shiny and new: Foldable
In recent versions of GHC many functions which used to work only on lists are now more generic. This includes map
and foldl
. If map in your GHCI has this type, then you have the new stuff:
map :: (Foldable t) => (a -> b) -> t a -> t b
Think of this t
as something like . For now and probably the rest of this course you can replace t a
with [a]
in your mind and in your own type definitions.
More information about this here on the Haskell wiki
Other Useful Links
Hoogle lookup of the type (a -> b) -> [a] -> [b]
GHC is full of lies - here is the blog post I mentioned.