Friday Session 2015-09-09

> 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]
 *FridaySession1> takeWhile (\n -> even n || n < 10) [0..]
 *FridaySession1> takeWhile (\n -> even n && n < 10) [0..]
 *FridaySession1> takeWhile (\n -> even n && n < 10) (map (*2) [0..]
     parse error (possibly incorrect indentation or mismatched brackets)
 *FridaySession1> takeWhile (\n -> even n && n < 10) (map (*2) [0..])

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
Prelude> let f = (\n -> n + 7)
Prelude> f 10
Prelude> f x

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):    

Now we see the difference:

 *FridaySession1> rem (-1) 2
 *FridaySession1> mod (-1) 2

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
Prelude> (*) 5 7

To turn a prefix function to an infix function, use ` `:

Prelude> 4 `elem` [1..]

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

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:


> bad_left_even :: (Int,Int) -> Bool
> bad_left_even (n,m) = (even n)

hlint says:

 FridaySession1.lhs:144:3: Suggestion: Use camelCase
   bad_left_even (n, m) = ...
 Why not:
   badLeftEven (n, m) = ...
 FridaySession1.lhs:144:25: Suggestion: Redundant bracket
   (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

GHCi on Acid


Hoogle lookup of reverse

Hoogle lookup of the type (a -> b) -> [a] -> [b]

GHC is full of lies - here is the blog post I mentioned.