2018-11-10 16:44
Page 1

Page 2

# Datatypes: recap

• From last week:
• ```data Suit = Spades | Hearts | Diamonds | Clubs

data Rank = Numeric Int | Jack | Queen | King | Ace

data Card = Card Rank Suit

data Hand = Empty | Add Card Hand```
Page 3

## Recursive datatypes

• `data Hand = Empty | Add Card Hand`
• The two constructors
`Empty`
and
`Add`
allows us to construct "lists" of cards of arbitrary length.
• `Empty`
• `Add c1 Empty`
• `Add c2 (Add c1 Empty)`
• `Add c3 (Add c2 (Add c1 Empty))`
• ...
Page 4

## Pattern matching

• ```colour :: Suit -> Colour
colour Clubs  = Black
colour _      = Red

rank :: Card -> Rank
rank (Card r s) = r

handBeats :: Hand -> Card -> Bool
handBeats Empty     beat = False
handBeats (Add c h) beat = cardBeats c beat || handBeats h beat```
• Functions on the values of a datatype are usually defined by pattern matching.
• The compiler knows what the alternatives are and can generate a warning if alternatives are missing. (`-Wincomple-patterns`)
• Recursive datatype => recursive functions (typically).
Page 5

# Lists: recap

• Haskell has a predefined type and special syntax for lists.
• Lists contain 0, 1, 2, or more values.
• `[]`
,
`[3]`
,
`["Haskell","JavaScript","C","Python"]`
• All values in a list have the same type
• `[1,3,True,"Python"]`
causes a type error.
• The order matters
• `[1,2,3] /= [3,2,1]`
• But how is the list type defined, what's special about it?
Page 6

# The List type, defined

• The natural way to define the list type would be like this:
• ```data List a = Nil | Cons a (List a)
```
• Types can have type parameters!
• The predefined list type is essentially the same, only with some special syntax:
• ```data [a] = [] | a : [a]
```
• So, all lists are built using two constructors:
• `[]`
, the empty list
• `x : xs`
, extending a list by adding one more value
`x`
to the front of an existing list
`xs`
.
Page 7

# List Syntax

• `5 : ( 6 : (3 : [])) == 5 : 6 : 3 : [] == [5,6,3]`
• `[]`
is often called nil
• `(:)`
is often called cons
• These names first appeared in Lisp 60 years ago.
• Because of Lisp, lists still have a central place in functional programming languages.
Page 8

# Strings and type synonyms

• ```type String = [Char]
```
• The `type` keyword introduces a type synonym, i.e. another name for an existing type.
• A string is a list of characters:
• `"Haskell" == ['H','a','s','k','e','l','l']`
Page 9

# Polymorphic list functions and their types

• Since the list type allow arbitrary element types, it is natural that many functions on lists are "generic" and allow arbitrary element types too. For example:
• ```length :: [a] -> Int
(++)   :: [a] -> [a] -> [a]
concat :: [[a]] -> [a]
take   :: Int -> [a] -> [a]
zip    :: [a] -> [b] -> [(a,b)]

map    :: (a -> b)    -> [a] -> [b]
filter :: (a -> Bool) -> [a] -> [a]
```
Page 10

# List functions for specific types

• There are also functions for specific types of lists. for example:
• ```and, or          :: [Bool]   -> Bool
words, lines     :: String   -> [String]
unwords, unlines :: [String] -> String```
• ```sum, product     :: Num a => [a] -> a
elem             :: Eq a  => a   -> [a] -> Bool
sort             :: Ord a => [a] -> [a]
```
• To use
`sort`
, `import Data.List`
Page 11

# Live Demo

## Let's look at some illustrative examples...

• length, sum
• append, reverse,
• take, drop, splitAt,
• zip, unzip,
• insert, isort, qsort
• QuickCheck: collect, classify, ==>
• Source code: WorkingWithLists.hs
Page 12

# Example: "Quicksort"

```qsort :: Ord a => [a] -> [a]
qsort [] = []
qsort (x1:xs) = qsort smaller ++ [x1] ++ qsort bigger
where
smaller = [x | x<-xs, x<=x1]
bigger  = [x | x<-xs, x>x1]
```
Page 13

# A pitfall which QuickCheck in GHCi

• ```prop_take_drop n xs = take n xs ++ drop n xs == xs

nonprop_take_drop n xs = drop n xs ++ take n xs == xs```
• `quickCheck prop_take_drop` +++ OK, passed 100 tests. `quickCheck nonprop_take_drop` +++ OK, passed 100 tests.
• Not good! Checking the types
• ```prop_take_drop :: Eq a => Int -> [a] -> Bool
nonprop_take_drop :: Eq a => Int -> [a] -> Bool```
• QuickCheck doesn't work on polymorphic types, somehow a more specific type got selected.
Page 14

# Extended defaulting in GHCi

• This means that
`quickCheck nonprop_take_drop`
will test
• `nonprop_take_drop :: Int -> [()] -> Bool`
• GHCi picks the unit type
`()`
, a type containing only one value
`()`
.
• ```data () = ()
```
• But when all elements in the list are the same, it doesn't matter in which order we put them!
• The test is good if we only care about the length of the lists.
Page 15

# Avoiding the pitfall which QuickCheck in GHCi

## Testing polymorphic functions without falling into the pitfall

• Use type signatures to avoid defaulting to the unit type.
• In the source code, or when calling
`quickCheck`
.
• Disable extended defaulting in GHCi to be safe:
• `:set -XNoExtendedDefaultRules`
• You will get en error message instead of a potentially meaningless test result.
• Or restrict the types used in defaulting to the standard ones:
• ```default (Integer,Double) ```