Abraham P
Abraham P

Reputation: 15481

Datatypes that are represented by a choice of one of many other datatypes

I am attempting to generate a deck for a toy implementation of Exploding Kittens.

Say I had the following types:

data ActionCard = Skip 
  | Attack 
  | Shuffle 
  | Nope 
  | Future 
  | Defuse 
  | Favor
  deriving Enum

data BasicCard = TacoCat 
  | MommaCat 
  | Catermelon 
  | BearCat 
  | PotatoCat 
  | BikiniCat 
  | RainboRalphingCat
  deriving Enum

data Card = ActionCard | BasicCard | BombCard

type Deck = [Card]

and a deck generator function like so:

generateDeck :: Int -> Deck
generateDeck players = (concat (map (replicate 5) [TacoCat ..])) 
  ++ (replicate 2 Nope)
  ++ (replicate 4 Skip)
  ++ (replicate 4 Attack)
  ++ (replicate 4 Shuffle)
  ++ (replicate 4 Future)
  ++ (replicate 1 Defuse)
  ++ (replicate 4 Favor)
  ++ (replicate (players + 1) BombCard)

This fails with:

Couldn't match expected type ‘[BasicCard]’
                    with actual type ‘a7 -> [a7]’
        Probable cause: ‘replicate’ is applied to too few arguments
        In the first argument of ‘(+)’, namely
          ‘replicate (length $ _players state)’
        In the second argument of ‘(++)’, namely
          ‘(replicate (length $ _players state) + 1 BombCard)’

(and similiar errors for hte other non basic cards)

That makes sense on one level as (concat (map (replicate 5) [TacoCat ..])) returns a [BasicCard], however I would have expected the function signature to force a more generic type?

How do allow for Card to be either an ActionCard, a BasicCard, or a BombCard?

Upvotes: 0

Views: 72

Answers (1)

Cactus
Cactus

Reputation: 27656

data Card = ActionCard | BasicCard | BombCard

This creates a new datatype Card with three constructors called ActionCard, BasicCard and BombCard. This has nothing to do with the other two datatypes that are called ActionCard or BasicCard; the namespace of types and constructors is distinct.

What you want to do is to define Card as either being an Action comprising an ActionCard, or a Basic BasicCard, or a BombCard:

data Card = Action ActionCard | Basic BasicCard | BombCard

then you can make your Deck by wrapping each card type in its correct constructor:

generateDeck :: Int -> Deck
generateDeck players = basics ++ actions ++ bombs
  where
    cats = concatMap (replicate 5 . Basic) [TacoCat ..]
    actions = map Action . concat $
              [ replicate 2 Nope
              , replicate 4 Skip
              , replicate 4 Attack
              , replicate 4 Shuffle
              , replicate 4 Future
              , replicate 1 Defuse
              , replicate 4 Favor
              ]
    bombs = replicate (players + 1) BombCard

Upvotes: 4

Related Questions