You want active templates :
let (|IsRoyalFlush|_|) hand = if isRoyalFlush hand then Some () else None let (|IsStraightFlush|_|) hand = if isStraightFlush hand then Some() else None // etc. match hand with | IsRoyalFlush -> 9 | IsStraightFlush -> 8 // etc.
Or better, pull all of this generic code into a simple active template constructor:
let toActivePattern pred x = if pred x then Some () else None let (|IsRoyalFlush|_|) = isRoyalFlush |> toActivePattern let (|IsStraightFlush|_|) = isStraightFlush |> toActivePattern // etc. match hand with | IsRoyalFlush -> 9 | IsStraightFlush -> 8 // etc.
If you donโt understand how this second example works, leave a comment and I go deeper.
Drawing up active patterns together
Since active templates are just functions, you can use the standard composition of functions to combine them. Well, almost a standard functional composition. A normal functional composition with the >> operator means "take the result of function 1 and use it as an input to function 2". But here function 1 and function 2 return Some () , but accept int; you cannot pass pin 1 to input 2 because they are not compatible. But we want to actually pass the same input to both functions and combine their output.
Therefore, instead of using the normal composition of functions, we define our own function, which takes two active templates and returns Some () if both templates correspond to the input:
let matchesBoth pattern1 pattern2 x = match pattern1 x with | None -> None | Some _ -> pattern2 x
And while we're on it, let's define a custom operator so you can see how it works. This matchesBoth function matchesBoth very similar to the && operator, because it will return Some () only if both patterns return Some () for any given input x . We do not have to overload the && operator to select a different type, so we will create a custom operator that looks like && but reminds us that it combines two active patterns. If our operator looks like |&&| It should be perfect. So let's create it:
let (|&&|) = matchesBoth
What is it! Now we can do something like:
let (|Div3|_|) n = if n % 3 = 0 then Some () else None let (|Div5|_|) n = if n % 5 = 0 then Some () else None let (|Div15|_|) = (|Div3|_|) |&&| (|Div5|_|) let fizzbuzz n = match n with | Div15 -> "FizzBuzz" | Div5 -> "Buzz" | Div3 -> "Fizz" | _ -> string n fizzbuzz 30 // Result: "FizzBuzz" fizzbuzz 31 // Result: "31"
Or for your example:
let (|IsStraightFlush|_|) = (|IsStraight|_|) |&&| (|IsFlush|_|)