Page 1

Page 2

# Lists: recap

• 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]`
Page 3

# List Syntax

• `5 : ( 6 : (3 : [])) == 5 : 6 : 3 : [] == [5,6,3]`
• `"Haskell" == ['H','a','s','k','e','l','l']`
• `type String = [Char]`
Page 4

# How is the list type defined?

• 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`
.
• So how is the type defined? We have seen a similar type:
• `data Hand = Empty | Add Card Hand`
• This is a type for list of cards. How do we generalize it to allow any type of elements?
Page 5

# 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]
```
Page 6

# 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 7

# 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```
• Overloaded list functions
• ```sum, product     :: Num a => [a] -> a
elem             :: Eq a => a -> [a] -> Bool
sort             :: Ord a => [a] -> [a]
```
Page 8

# Live Demo

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

• append, reverse,
• sum, product,
• take, drop, splitAt,
• zip, unzip,
• insert, isort, qsort
• QuickCheck: collect, classify
• Source code: WorkingWithLists.hs
Page 9

# 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 10

# 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 11

# Extended defaulting in GHCi

• GHCi tries to be helpful by choosing a specific type instead of complaining about unresolved overloading.
• 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 12

# 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) ```