@jozefg's answer is probably the easiest way, but an alternative that could make other operations easier is to defined Enum
and Bounded
instances for Suit
and Rank
:
data Suit = Hearts | Spades | Diamonds | Clubs deriving (Eq, Show, Enum, Bounded)
data Rank = Numeric Integer | Jack | Queen | King | Ace deriving (Eq, Show)
data Card = Card Rank Suit deriving (Eq, Show)
data Deck = None | Cons Card Deck deriving (Eq, Show)
instance Bounded Rank where
minBound = Numeric 2
maxBound = Ace
instance Enum Rank where
toEnum n
| n <= 1 = error "Invalid card value"
| n <= 10 = Numeric $ toInteger n
| n == 11 = Jack
| n == 12 = Queen
| n == 13 = King
| n == 14 = Ace
| otherwise = error "Invalid card value"
fromEnum (Numeric i) = fromEnum i
fromEnum Jack = 11
fromEnum Queen = 12
fromEnum King = 13
fromEnum Ace = 14
range :: (Bounded a, Enum a) => [a]
range = [minBound .. maxBound]
fromList :: [Card] -> Deck
fromList = foldr Cons None
fullDeck :: Deck
fullDeck = fromList [Card r s | r <- range, s <- range]
It's rather easy to let the compiler just derive the instances for Suit
, but it's a little more involved (but straightforward) for Rank
. Strictly speaking, you don't need range
, but I think it's kinda cool. Another advantage is that you can do things like [Numeric 2, Numeric 5, .. Ace]
and get back [Numeric 2, Numeric 5, Numeric 8, Jack, Ace]
.
EDIT
If you can't change the definition of the data types to add the deriving statements, you can do it like this:
{-# LANGUAGE StandaloneDeriving #-}
data Suit = Hearts | Spades | Diamonds | Clubs
-- etc
deriving instance Eq Suit
deriving instance Show Suit
deriving instance Enum Suit
deriving instance Bounded Suit