Search code examples
haskellcartesian-productalgebraic-data-types

Haskell: How to generate a cartesian product of two simple algebraic data types


I am learning Haskell, so I'm writing some simple card games. I've defined some data types:

data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord)

data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show)

data Card = Card Rank Suit 

Now I'd like to create a pristine deck of 52 cards. I'm sure there is a slick way to do it, but all I can come up with is:

 pristineDeck = [Card Ace Hearts, Card Two Hearts, ...]

Can I get Haskell to generate this list for me?


Solution

  • List comprehensions are a very tidy syntax for this. If you derive Enum on Rank and Suit you can express it very simply as:

    pristineDeck = [ Card rank suit | suit <- [Hearts .. Clubs], rank <- [Ace .. King] ]
    

    If you're wondering why I have suit and rank in different orders, the first is because of the order the Card constructor uses, while the latter is to get the order of the resulting list--suits together in ascending order.

    In more generality, or when a single list comprehension gets too bulky, the cartesian product is exactly the behavior given by the Monad instance for lists. The following is equivalent to the list comprehension above:

    pristineDeck = do suit <- [Hearts .. Clubs]
                      rank <- [Ace .. King]
                      return $ Card rank suit
    

    As one other minor point, to save yourself the trouble of remembering what order the Suit values are in, deriving Bounded as well will enable to write [minBound .. maxBound] to enumerate all values of any type with instances of both Enum and Bounded.