2018-11-13 19:12
Page 1

Page 2

# Modelling Data

• A big part of designing software is modelling the data in an appropriate way.
• A lot of data can be represented as numbers or strings, but there is often better alternatives.
• Today's lecture: we look at how to model data by defining new types in Haskell.
Page 3

# Modelling Playing Cards

• Consider playing cards used in card games.
• Every card has a suit. ♠ ♥ ♦
• We can defined a new type for suits:
• `data Suit = Spades | Hearts | Diamonds | Clubs`
• Note that both the names of types (here
`Suit`
Page 4

Page 5

# Showing Values

• `Spades` <interactive>:3:1: error: • No instance for (Show Suit) arising from a use of ‘print’ • In a stmt of an interactive GHCi command: print it `:i print` print :: Show a => a -> IO () -- Defined in ‘System.IO’
• Fix
• ```data Suit = Spades | Hearts | Diamonds | Clubs
deriving Show```
Page 6

# The colours of cards

• Each suit has a colour – red or black
• Model colours by a type
• ```data Colour = Black | Red
deriving Show```
• Define functions by pattern matching
• ```colour :: Suit -> Colour
colour Hearts   = Red
colour Diamonds = Red
colour Clubs    = Black```
• One equation per case in the type of the argument
Page 7

# Combining similar cases

• ```colour :: Suit -> Colour
colour Clubs  = Black
colour s      = Red```
• Or, by using the wild card (don't care) pattern:
• ```colour :: Suit -> Colour
colour Clubs  = Black
colour _      = Red```
• Useful if there are many cases, but only a few of them needs to be treated separately.
Page 8

# Alternative notation for case analysis

• ```colour :: Suit -> Colour
colour suit =
case suit of
Hearts   -> Red
Diamonds -> Red
Clubs    -> Black```
• Less repetitive...
• Useful if you need to do case analysis on values other than the function arguments, e.g.
• ```f suit = case colour suit of
Red   -> (...)
Black -> (...)
```
Page 9

# Incomplete patterns

• What happens if some cases are missing (by mistake)?
• ```colour :: Suit -> Colour
colour Clubs  = Black
coluor _      = Red```
• GHCi does not report an error, but the function
`colour`
can crash when we use it:
• `colour Diamonds` *** Exception: DataTypes.hs:(19,1)-(20,23): Non-exhaustive patterns in function colour
Page 10

## There is a warning you can turn on to detect this:

• `:set -Wincomplete-patterns`   `:load DataTypes.hs` DataTypes.hs:19:1: warning: [-Wincomplete-patterns] Pattern match(es) are non-exhaustive In an equation for ‘colour’: Patterns not matched: Hearts Diamonds
• Another warning that could be useful: `-Wmissing-signatures`
Page 11

# The Ranks of Cards

• Cards have ranks: 2, 3 .. 10, J, Q, K, A
• ```data Rank = Numeric Int | Jack | Queen | King | Ace
deriving Show```
• `:t Numeric` Numeric :: Int -> Rank `Numeric 3` Numeric 3
• Constructors are not just atomic units, they can carry additional information.
Page 12

# Rank Beats Rank

• `rankBeats :: Rank -> Rank -> Bool`
• How many cases do we need to consider?
• There are five cases in the
`Rank`
type.
• We have two arguments of type
`Rank`
.
Page 13
##### Rank Beats Rank
```rankBeats :: Rank -> Rank -> Bool
rankBeats _ Ace = False    -- nothing beats an Ace
```
Page 14
##### Rank Beats Rank
• ```rankBeats :: Rank -> Rank -> Bool
rankBeats _ Ace = False   -- nothing beats an Ace
rankBeats Ace _ = True    -- an Ace beats anything else
```
• The second equation is used only if the first equation doesn't match.
Page 15
##### Rank Beats Rank
```rankBeats :: Rank -> Rank -> Bool
rankBeats _ Ace = False   -- nothing beats an Ace
rankBeats Ace _ = True    -- an Ace beats everything else
rankBeats _ King = False
rankBeats King _ = True
rankBeats _ Queen = False
rankBeats Queen _ = True
rankBeats _ Jack = False
rankBeats Jack _ = True```
Page 16
##### Rank Beats Rank
```rankBeats :: Rank -> Rank -> Bool
rankBeats _ Ace = False   -- nothing beats an Ace
rankBeats Ace _ = True    -- an Ace beats everything else
rankBeats _ King = False
rankBeats King _ = True
rankBeats _ Queen = False
rankBeats Queen _ = True
rankBeats _ Jack = False
rankBeats Jack _ = True
rankBeats (Numeric m) (Numeric n) = m > n```
Page 17

Page 18

# Alternative solution

• ```data Rank = Numeric Int | Jack | Queen | King | Ace
deriving (Eq,Ord,Show)

rankBeats :: Rank -> Rank -> Bool
rankBeats r1 r2 = r1>r2```
• The ordering we want can often be obtained by using `deriving Ord`.
• Put the alternatives in the data definition in the right order!
Page 19

# Alternative Rank type

• `Numeric n `
is a valid rank only if
`n`
is in
`[2..10]`
, but nothing stops us from creating values like
`Numeric 0 `
or
`Numeric 35`
. Consider this alternative type:
• ```data Rank = N2 | N3 | N4 | N5 | N6 | N7 | N8 | N9 | N10
| Jack | Queen | King | Ace
deriving (Eq,Ord,Show,Enum)
```
• Two advantages: it's junk-free. It is an enumeration type.
• `fromEnum N2` 0 `[N9 .. Ace]` [N9,N10,Jack,Queen,King,Ace]
Page 20

# Modelling a Card

• A Card has both a Rank and a Suit
• ```data Card = Card Rank Suit
deriving Show```
• Functions to inspect the rank and suit of a card:
• ```rank :: Card -> Rank
rank (Card r s) = r

suit :: Card -> Suit
suit (Card r s) = s```
Page 21

# A useful shortcut

• ```data Card = Card {rank::Rank, suit:: Suit}
deriving Show

example_card_2 = Card {rank=King, suit=Hearts}
```
• Defines the same data type and the functions `rank` and `suit` at the same time.
• Also affects how values are shown.
Page 22

# When does one card beat another card?

• When both cards have the same suit and the rank is higher:
• ```cardBeats :: Card -> Card -> Bool
cardBeats (Card r1 s1) (Card r2 s2) = s1==s2 && rankBeats r1 r2```
• Need equality tests for the `Suit` type
• ```data Suit = Spades | Hearts | Diamonds | Clubs
deriving (Eq,Show)
```
Page 23

# Alternative implementations of cardBeats

• As before:
• ```cardBeats :: Card -> Card -> Bool
cardBeats (Card r1 s1) (Card r2 s2) = s1==s2 && rankBeats r1 r2```
• Alternative:
• ```cardBeats :: Card -> Card -> Bool
cardBeats card1 card2 = suit card1 == suit card2
&& rankBeats (rank card1) (rank card2)
```
• Which do you prefer?
Page 24

# Modelling a Hand of Cards 1

• A hand may contain any number of cards, from zero and up!
• A natural choice is to use a list:
• ```type Hand = [Card]
```
• For illustrative purposes, we will define our own list-like type instead.
Page 25

# Modelling a Hand of Cards

## Break it down into two cases

• An empty hand
• Adding one more card to an existing hand
• ```data Hand = Empty | Add Card Hand
deriving Show```
• A recursive type!
Page 26

# When can a hand beat a card?

• An empty hand beats nothing
• A non-empty hand can beat a card if the first card can, or if the rest of the hand can!
• ```handBeats :: Hand -> Card -> Bool
handBeats Empty card = False
handBeats (Add c h) card = cardBeats c card || handBeats h card```
• A recursive function!
Page 27

# Which card to play?

• Given a card to beat and a hand
• Choose a card that can beat the card to beat, if possible.
• Choose the lowest card that beats the card to beat.
• If you can follow suit, you must do that.
• Choose the lowest card of the same suit.
• Otherwise, choose the lowest card
• `chooseCard :: Card -> Hand -> Card`
Page 28
##### Which card to play?
• ```chooseCard :: Card -> Hand -> Card
chooseCard beat hand
| handBeats hand beat       = lowestCard (betterCards hand beat)
| haveSuit hand (suit beat) = lowestCard (sameSuit hand (suit beat))
| otherwise                 = lowestCard hand```
• Helper functions:
• ```haveSuit    :: Hand -> Suit -> Bool
lowestCard  :: Hand -> Card
betterCards :: Hand -> Card -> Hand
sameSuit    :: Hand -> Suit -> Hand```
Page 29

# Recap

• Modelling the problem using datatypes with components
• Using recursive datatypes to model things of varying size
• Using recursive functions to manipulate recursive data types