Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Pattern Matching Nested Discriminated Unions

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.

like image 534
dlkmp Avatar asked Aug 25 '17 18:08

dlkmp


3 Answers

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
   ...
like image 53
Fyodor Soikin Avatar answered Oct 09 '22 08:10

Fyodor Soikin


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
  ...
like image 26
Chad Gilbert Avatar answered Oct 09 '22 08:10

Chad Gilbert


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
like image 32
Soldalma Avatar answered Oct 09 '22 09:10

Soldalma