I'm trying to write a function that creates a full deck of cards (52 with no Jokers). I'm still new to this, I'm thinking that some sort of list comprehension is what I need to do, but I don't know quite how to write it (see createHandFromList
function). I'm also guessing it needs to be recursive so that it will keep going. Anyway, here's what my code looks like so far, any help and advice is, as always, much appreciated.
data Card = Card Rank Suit
deriving (Eq, Show)
data Rank = Numeric Integer | Jack | Queen | King | Ace
deriving (Eq, Show)
data Suit = Hearts | Spades | Diamonds | Clubs
deriving (Eq, Show)
data Hand = Empty | Add Card Hand
deriving (Eq, Show)
fullDeck :: Hand
fullDeck = createHandFromList (suitedCardList Clubs ++
suitedCardList Diamonds ++
suitedCardList Hearts ++
suitedCardList Spades)
suitedCardList :: Suit -> [Card]
suitedCardList s = [Card (Numeric 2) s, Card (Numeric 3) s,
Card (Numeric 4) s, Card (Numeric 5) s,
Card (Numeric 6) s, Card (Numeric 7) s,
Card (Numeric 8) s, Card (Numeric 8) s,
Card (Numeric 10) s, Card Jack s,
Card Queen s, Card King s, Card Ace s]
createHandFromList :: [Card] -> Hand -> Hand
createHandFromList [c:cs] h = [Add card h | card <- c]
Currently, the code returns this as an error:
BlackJack.hs:107:21: error:
• Couldn't match expected type ‘Card’ with actual type ‘[[Card]]’
• In the pattern: c : cs
In the pattern: [c : cs]
In an equation for ‘createHandFromList’:
createHandFromList [c : cs] h = [Add card h | card <- c]
|
107 | createHandFromList [c:cs] h = [Add card h | card <- c] | ^^^^
BlackJack.hs:107:31: error:
• Couldn't match expected type ‘Hand’ with actual type ‘[Hand]’
• In the expression: [Add card h | card <- c]
In an equation for ‘createHandFromList’:
createHandFromList [c : cs] h = [Add card h | card <- c]
|
107 | createHandFromList [c:cs] h = [Add card h | card <- c] |
There are multiple problems with this code. Firstly, you have used [c:cs]
as a pattern in the definition of createHandFromList
. However, this is a syntax error: in pattern matching, the syntax is (c:cs)
. This gives the following code:
createHandFromList :: [Card] -> Hand -> Hand
createHandFromList (c:cs) h = [Add card h | card <- c]
But there is still another problem with this code. You return a list of Hand
s, but the type signature says that you must return a single Hand
! Clearly this is wrong. To figure out what to do instead, let’s take a step back and work through exactly what createHandFromList
should do:
This can be done most easily with recursion:
createHandFromList :: [Card] -> Hand -> Hand
-- Add card ‘c’ to the result of (createHandFromList cs h)
createHandFromList (c:cs) h = Add c (createHandFromList cs h)
-- Return hand ‘h’ if there are no more cards left to add
createHandFromList [] h = h
There is a small simplification I want to address as well. (Credit goes to @Khuldraeseth na'Barya, who wrote this in the comments.) Let’s look at the possible values of Hand
:
Empty
Add card1 Empty
Add card2 (Add card1 Empty)
Add card3 (Add card2 (Add card1 Empty))
This looks exactly like a list of Card
s! And it turns out that the types Hand
and [Card]
are isomorphic. In pseudo-Haskell:
data Hand = Empty | Add Card Hand
data [Card] = [] | : Card [Card]
So, if you represent Hand
as something like newtype Hand = Hand { getCards :: [Card] }
, then you should be able to replace the recursion by a list comprehension. This is left as an exercise for the reader.