4
\$\begingroup\$

I'm learning F# by doing various small projects. One of them is a problem where the program reads Poker hands and rates them. It's Texas Hold'Em, so for each player it tries every five card selection from seven cards.

When it's time for scoring I have made a set of functions that each evaluate a hand and produces an Score option of whether that hand matches or not:

type Score =
{ Points: int
  Desc: string }

val tryStraightFlush: PokerHand -> Score option
val tryFourOfAKind: PokerHand -> Score option
// more functions ..      
val getHighCard: PokerHand -> Score option

type PokerHand =
  class
    new : cards:Card list -> PokerHand
    member Cards : Card list
    member HighCard : Card option
  end

But now I have a problem, because I need to use the options that these functions produce, so I cannot do a match like this:

//Non-working code. 
//Something roughly similar to this would be nice.
//But I need to use the values from the "try"-functions
//and they get thrown away here:

let scoreCards (hand: PokerHand) =
match hand with
| r when (tryStraightFlush hand) <> None -> //prod. Score 
| r when (tryFourOfAKind hand) <> None -> //prod. Score 
// more code

Instead my code ends up looking like this, and I have to say I don't like this deep nesting.. How can I have a small function for testing each type of configuration, while avoiding a deeply nested if-expressions?

let scoreCards (hand: PokerHand) =
  // Too much nesting!
  let r = tryStraightFlush hand
  if r <> None then
    r.Value 
  else
    let r = tryFourOfAKind hand
    if r <> None then
        r.Value
    else
        let r = tryFullHouse hand
        if r <> None then
            r.Value
        else
            let r = tryFlush hand
            if r <> None then
                r.Value
            else
                // nesting continues..
\$\endgroup\$
  • 2
    \$\begingroup\$ You want to look at active patterns \$\endgroup\$ – John Palmer Oct 4 '16 at 7:30
  • 1
    \$\begingroup\$ @JohnPalmer Why you do not want to write the answer? \$\endgroup\$ – user110704 Oct 4 '16 at 9:04
  • 2
    \$\begingroup\$ Check this out stackoverflow.com/questions/39262053/… \$\endgroup\$ – asibahi Oct 5 '16 at 1:12
3
\$\begingroup\$

The solution here is to use an active pattern, which enables you to create a custom matching function.

You can even reuse your existing functions.

It would look like

let |StraightFlush|_| hand = tryStraightFlush hand

then you can use it like this

match hand with
|Straightflush(score) -> ...
| ....
\$\endgroup\$
  • \$\begingroup\$ Thank you very much @john-palmer. I didn't learn about active patterns until now, but I see that they are indeed the way to do this. \$\endgroup\$ – Grubl3r Oct 4 '16 at 9:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.