Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# modeling playing cards

I am trying to represent standard playing cards in F#. My goal is to implement a clone of Microsoft Solitaire (the one that comes with Windows) , a game in which Cards' Suit, Face, and Color are important. This exercise is mostly intended as a way to learn some F#.

I have considered using discriminated unions:

type Suit =
    | Diamonds
    | Hearts
    | Clubs
    | Spades

type Color =
    | Red
    | Black

type Face =
    Two | Three | Four | Five | Six | Seven |
    Eight | Nine | Ten | Jack | Queen | King | Ace

with a record type for Card:

type Card = {
    suit: Suit;
    face: Face;
    color: Color;
}

However, a Card's Color can be inferred from its Suit—all Diamonds and Hearts are Red, and all Clubs and Spades are Black. Suit cannot be determined from Color alone. Perhaps something like this is appropriate:

type Suit =
    | Diamonds of Color //should always be red
    | Hearts of Color //should always be red
    | Clubs of Color //should always be black
    | Spades of Color //should always be black

type Face =
    Two | Three | Four | Five | Six | Seven |
    Eight | Nine | Ten | Jack | Queen | King | Ace

type Card = {
    suit: Suit;
    face: Face;
}

But this doesn't seem right, since this allows incorrect combinations, e.g. Black Hearts and Red Spades.

My questions are:

  1. What is the most idiomatic way to handle Suit and Color, considering that Color is dependent on Suit?
  2. Should the concept of Color even be explicitly represented? One could theoretically just replace all occurrences of Color with pattern matches against Diamonds or Hearts (which are red) and Clubs or Spades (which are black).
like image 512
theguy Avatar asked Mar 12 '15 04:03

theguy


2 Answers

Since Color can always be inferred from Suit, there's no reason to model it explicitly; you'll want to make illegal states unrepresentable.

You can still get a nice programming experience out of your model, and have a nice way of modelling colour, using an Active Pattern:

type Suit =
    | Diamonds
    | Hearts
    | Clubs
    | Spades

let (|Red|Black|) suit =
    match suit with
    | Diamonds | Hearts -> Red
    | Clubs | Spades -> Black

This would enable you to pattern match on Suit, like this inane example:

let printColor card =
    match card.Suit with
    | Red -> "Red"
    | Black -> "Black"

Usage example from FSI:

> printColor { Suit = Spades; Face = Ace };;
val it : string = "Black"
> printColor { Suit = Diamonds; Face = King };;
val it : string = "Red"
like image 119
Mark Seemann Avatar answered Oct 22 '22 13:10

Mark Seemann


You can add a recording method:

type Card = 
    {suit: Suit;face: Face}
    member this.Color = 
        match this.suit with
            | Diamonds | Hearts -> Red
            | Clubs | Spades -> Black

Example:

let v = {suit = Diamonds;face = Two}
printfn "%A" v.Color

let v1 = {suit = Clubs;face = Two}
printfn "%A" v1.Color

Red Black Для продолжения нажмите любую клавишу . . .

like image 26
FoggyFinder Avatar answered Oct 22 '22 12:10

FoggyFinder