5
votes

I've a nested discriminated union representing a deck of playing cards:

type Symbol =
| Seven
| Eight
| Nine
| Ten
| Jack
| Queen
| King
| Ace

type Card =
| Heart of Symbol
| Diamond of Symbol
| Spade of Symbol
| Club of Symbol

Now I want to write a function returning the value of a given card which in my case is independent of the cards suit:

let GetValue (card : Card) =
  match card with
  | Heart(Seven) -> 0
  | Diamond(Seven) -> 0
  | Spade(Seven) -> 0
  | Club(Seven) -> 0
  ...

This is obviously tedious to write. Is there a way to do something like this

let GetValue (card : Card) =
  match card with
  | _(Seven) | _(Eight) | _(Nine) -> 0
  | _(Ten) -> 10
 ...

Thanks a lot.

3

3 Answers

11
votes

You're modeling your data incorrectly. Since the total deck is the full Cartesian product of suits and ranks, it doesn't make sense to "wrap" ranks in suits. The two should be independent, equally important attributes of a card:

type Rank =
  | Seven
  | Eight
  | Nine
  | Ten
  | Jack
  | Queen
  | King
  | Ace

type Suit =
  | Heart
  | Diamond
  | Spade
  | Club

type Card = { suit: Suit; rank: Rank }

let getValue (card:Card) = 
   match card.rank with
   | Seven | Eight | Nine -> 0
   | Ten -> 10
   ...
6
votes

You won't be able to bypass constructor matching in that fashion, but you can remove some tedium by creating a function to pull the symbol out of a card:

let symbol card =
  match card with
  | Heart(s) -> s
  | Diamond(s) -> s
  | Spade(s) -> s
  | Club(s) -> s

let GetValue (card : Card) =
  match symbol card with
  | Seven | Eight | Nine -> 0
  | Ten -> 10
  ...
1
votes

Here is another way:

let set789 = [Seven; Eight; Nine] |> Set.ofList
let cardValue x =
    match x with
    | y when Set.contains y set789 -> 0
    | Ten   ->  1
    | Jack  ->  2
    | Queen ->  3
    | King  ->  4
    | Ace   ->  5
    | _     -> -1

let GetValue (card : Card) =
  match card with
  | Club(x) | Spade(x) | Diamond(x) | Heart(x) -> cardValue x