Alex
Alex

Reputation: 434

How to transform a list of enums to another type of enum in Haskell?

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

Upvotes: 1

Views: 337

Answers (1)

bradrn
bradrn

Reputation: 8467

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 Hands, 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:

  • Starting with the hand it is given, add one card to that hand
  • Add another card to the hand just created
  • Add another card to the hand just created
  • Repeat until no cards are left.

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))
  • etc.

This looks exactly like a list of Cards! 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.

Upvotes: 2

Related Questions