Shorten F# Tuple Matching - f#

F# beginner here.
I am trying to match a tuple with several cases and return a specific value on the matching condition. This is how it looks:
match inf, sup with
| nan , _
| _ , nan
-> Interval(nan,nan)
| _ , _ when inf = -infinity && sup = -infinity -> Interval(nan,nan)
| _ , _ when inf = infinity && sup = infinity -> Interval(nan,nan)
| _ , _ when inf > sup -> Interval(nan,nan)
| _ , _ -> Interval(inf,sup)
Since almost every case returns Interval(nan,nan) I would like to group them due to readability, but I have no idea how. I tried the following
match inf, sup with
| nan , _
| _ , nan
| _ , _ when inf = -infinity && sup = -infinity
| _ , _ when inf = infinity && sup = infinity
| _ , _ when inf > sup -> Interval(nan,nan)
-> Interval(nan,nan)
| _ , _ -> Interval(inf,sup)
but the compiler says
The two sides of this 'or' pattern bind different sets of varibales
So I tried the following:
match inf, sup with
| nan , _
| _ , nan
-> Interval(nan,nan)
| _ , _ when inf = -infinity && sup = -infinity
| _ , _ when inf = infinity && sup = infinity
| _ , _ when inf > sup -> Interval(nan,nan)
-> Interval(nan,nan)
| _ , _ -> Interval(inf,sup)
Here I get an error at the second | from the second when clause. He expects an '->' or other token.
So: How can I shorten this matching or how can I improve it? Those several Interval(nan,nan) seem uneccesary to me.
Thanks in advance!

You are not checking nan correctly. Matching against nan will result in any supplied value being bound to a value called nan. Notice in FSI:
let isNan x = match x with |nan -> true |_ -> false;;
let isNan x = match x with |nan -> true |_ -> false;;
-----------------------------------------^
stdin(1,42): warning FS0026: This rule will never be matched
You should check for nan values by using System.Double.IsNaN(x).
Bearing this in mind, I would use an active pattern to check for non-valid values:
let (|PositiveInfinity|NegativeInfinity|NaN|Real|) = function
|v when v = -infinity -> NegativeInfinity
|v when v = infinity -> PositiveInfinity
|v when System.Double.IsNaN(v) -> NaN
|v -> Real v
Then reduce your pattern match to:
let test inf sup =
match inf, sup with
|NaN, _
|_, NaN
|NegativeInfinity, NegativeInfinity
|PositiveInfinity, PositiveInfinity
|_, _ when inf > sup -> Interval(nan, nan)
|_, _ -> Interval(inf, sup)

In match statement when clause is common for all or cases that are listed before. If you want to visualize this, you can think like there are parens like this:
( | Case1 n
| Case2 n
| Case3 n ) when predicate(n) ->
You cannot repeat when clause few times before you have result expression (after ->).
Instead you could join all conditions in one when clause, something similar to:
| _ when cond1 ||
cond2 ||
cond3 -> ...

The answer given by #TheInnerLight is great, but handles a corner case differently. The OP considered an interval between (-inf, 10.0) as valid, but the posted answer does not. Here's an answer that handles all cases the same:
let (|PositiveInfinity|NegativeInfinity|NaN|Real|) = function
| v when Double.IsPositiveInfinity v -> PositiveInfinity
| v when Double.IsNegativeInfinity v -> NegativeInfinity
| v when Double.IsNaN v -> NaN
| v -> Real v
let getInterval inf sup =
match inf, sup with
| NaN, _
| _ , NaN
| NegativeInfinity, NegativeInfinity
| PositiveInfinity, PositiveInfinity
| _ , _ when inf > sup -> Interval(nan,nan)
| _ , _ -> Interval(inf,sup)
Slightly off topic: I would generally advise against using NaN as a marker for invalid intervals. Any comparisons with NaN return false, meaning you need to handle them differently in each interval check. Consider the following 2 versions of writing "is a point v inside an interval?", where lower and upper bound happen to be NaN:
let inside1 v = v >= nan && v <= nan
let inside2 v = not (v < nan || v > nan)
In inside1, you check "is the point inside?", in inside2, you ask "is it not outside?". Checking the point 1.0 gives:
> inside1 1.0;;
val it : bool = false
> inside2 1.0;;
val it : bool = true
I would hence suggest that your function returns an Interval option, meaning None in all cases where you presently return Interval(nan,nan). Otherwise, your code will be littered by explicit NaN checks all over.

Just use the Boolean or || in the when clauses:
match inf, sup with
| nan , _
| _ , nan
-> Interval(nan,nan)
| _ , _ when inf = -infinity && sup = -infinity ||
inf = infinity && sup = infinity ||
inf > sup -> Interval(nan,nan)
| _ , _ -> Interval(inf,sup)

Related

Is that a proper use of match with in F# or is there a more idiomatic way to accomplish the same

The code is getting a json string from a server and parses it into a jObject and then branches appropriately.
let j = JObject.Parse x
match x, j with
| _ when x = "pong" -> ()
| _ when j.ContainsKey "table" -> HandleTableMessages j x
| _ when j.ContainsKey "success" -> HandleSuccessMessages j
| _ when j.ContainsKey "error" -> HandleErrorMessages j
| _ when j.ContainsKey "info" -> j.SelectToken "info" |> string |> this.Print
| _, null -> this.Error ("malformed message: " + x)
| _ -> this.Error("unknown message type: " + x)
I think there is something a little bit heavy with the _ when part and I am wondering if there a better use of the F# grammar to express this?
It's a good sign that you realize this code is bad. It shows you may have better taste than most beginners. Using the simplest structure for a task is very important.
match x, j with
| _ when x = "pong" -> ()
...
First note that (x,j) is unused, so this simplifies to:
match () with
| _ when x = "pong" -> ()
...
Then you can realize that matching on a unit is silly, and that you should have used a simpler statement:
if x = "pong" then ()
elif j.ContainsKey "table" then HandleTableMessages j x
...
else this.Error("unknown message type: " + x)
I have just noticed that elif is not in the F# cheatsheet so I will try to get it added there as it's a basic keyword.
// Active Pattern
let (|Table|Success|Error|Info|Unknown|) (j: Newtonsoft.Json.Linq.JObject) =
if j.ContainsKey "table" then Table
elif j.ContainsKey "success" then Success
elif j.ContainsKey "error" then Error
elif j.ContainsKey "info" then Info
else Unknown
match x, j with
| "pong", _ -> ()
| _, Table -> HandleTableMessages j x
| _, Success -> HandleSuccessMessages j
| _, Error -> HandleErrorMessages j
| _, Info -> j.SelectToken "info" |> string |> this.Print
| _, null -> this.Error ("malformed message: " + x)
| _, Unknown
| _, _ -> this.Error("unknown message type: " + x)

Why isn't pattern matching on an assigned value recognized [duplicate]

This question already has answers here:
Why doesn't pattern matching on a property of a record compile?
(2 answers)
Closed 6 years ago.
Why isn't pattern matching on an assigned value recognized
I receive a warning when I attempt to pattern match on the value called target:
[<Test>]
let ``set center cell to alive``() =
// Setup
let target = (2,2)
let grid = createGrid 9 |> Map.map (fun k v ->
match k with
| target -> { v with Status=Alive }
| _ -> v)
// Test
let center = grid |> getStatus (2,2)
// Verify
center |> should equal Alive
The warning points to:
| target -> { v with Status=Alive }
| _ -> v)
Specifically on:
| _ -> v)
The warning is:
This rule will never be reached.
Which forces me to not use target and instead hard-code the value in order to resolve the warning:
[<Test>]
let ``set center cell to alive``() =
// Setup
let grid = createGrid 9 |> Map.map (fun k v ->
match k with
| (2,2) -> { v with Status=Alive }
| _ -> v)
// Test
let center = grid |> getStatus (2,2)
// Verify
center |> should equal Alive
Can someone explain why I can't do this?
Full code:
type Status = Alive | Dead
type Cell = { X:int; Y:int; Status:Status }
let isNeighbor cell1 cell2 =
let isAbsNeighbor v1 v2 =
match abs (v1 - v2) with
| 0 | 1 -> true
| _ -> false
let isValueNeighbor v1 v2 =
match v1 >= 0
&& v2 >= 0 with
| true -> isAbsNeighbor v1 v2
| _ -> isAbsNeighbor v2 v1
match cell1.X <> cell2.X
|| cell1.Y <> cell2.Y with
| true -> isValueNeighbor cell1.X cell2.X
&& isValueNeighbor cell1.Y cell2.Y
| _ -> false
let createGrid rowCount =
[for x in 1..rowCount do
for y in 1..rowCount do
yield { X=x; Y=y; Status=Dead } ]
|> List.map (fun c -> (c.X, c.Y), { X=c.X; Y=c.Y; Status=Dead })
|> Map.ofList
let getStatus coordinate (grid:Map<(int * int), Cell>) =
match grid.TryFind coordinate with
| Some cell -> cell.Status
| None -> Dead
In a match expression, the rule
match k with
| target -> { v with Status=Alive }
unconditionally matches and binds k to a name target which shadows the existing definition. This means the following clause will never be reached. You can use a conditional match:
match k with
| t when t = target -> { v with Status = Alive }
| _ -> v
According to Pattern Matching your target is a variable pattern, so it shadows the original target value.
Pattern matching is useful for destructuring the matched object, for a simple test plain if-else is preferrable (in my opinion).
A use-case for pattern matching would be if you want to test several cases. Instead of a when guard you could then also use active patterns:
let (|Eq|_|) expected actual =
if expected = actual then Some()
else None
let target = (2,2)
let attempt = (3,2)
match attempt with
| Eq target -> Some "Bulls eye"
| (2, _) -> Some "Almost"
| t when fst t > 20 -> Some "Quite the contrary"
| _ -> None

How to fix this bug in a math expression evaluator

I've written a typical evaluator for simple math expressions (arithmetic with some custom functions) in F#. While it seems to be working correctly, some expressions don't evaluate as expected, for example, these work fine:
eval "5+2" --> 7
eval "sqrt(25)^2" --> 25
eval "1/(sqrt(4))" --> 0.5
eval "1/(2^2+2)" --> 1/6 ~ 0.1666...
but these don't:
eval "1/(sqrt(4)+2)" --> evaluates to 1/sqrt(6) ~ 0.408...
eval "1/(sqrt 4 + 2)" --> will also evaluate to 1/sqrt(6)
eval "1/(-1+3)" --> evaluates to 1/(-4) ~ -0.25
the code works as follows, tokenization (string as input) -> to rev-polish-notation (RPN) -> evalRpn
I thought that the problem seems to occur somewhere with the unary functions (functions accepting one operator), these are the sqrt function and the negation (-) function. I don't really see what's going wrong in my code. Can someone maybe point out what I am missing here?
this is my implementation in F#
open System.Collections
open System.Collections.Generic
open System.Text.RegularExpressions
type Token =
| Num of float
| Plus
| Minus
| Star
| Hat
| Sqrt
| Slash
| Negative
| RParen
| LParen
let hasAny (list: Stack<'T>) =
list.Count <> 0
let tokenize (input:string) =
let tokens = new Stack<Token>()
let push tok = tokens.Push tok
let regex = new Regex(#"[0-9]+(\.+\d*)?|\+|\-|\*|\/|\^|\)|\(|pi|e|sqrt")
for x in regex.Matches(input.ToLower()) do
match x.Value with
| "+" -> push Plus
| "*" -> push Star
| "/" -> push Slash
| ")" -> push LParen
| "(" -> push RParen
| "^" -> push Hat
| "sqrt" -> push Sqrt
| "pi" -> push (Num System.Math.PI)
| "e" -> push (Num System.Math.E)
| "-" ->
if tokens |> hasAny then
match tokens.Peek() with
| LParen -> push Minus
| Num v -> push Minus
| _ -> push Negative
else
push Negative
| value -> push (Num (float value))
tokens.ToArray() |> Array.rev |> Array.toList
let isUnary = function
| Negative | Sqrt -> true
| _ -> false
let prec = function
| Hat -> 3
| Star | Slash -> 2
| Plus | Minus -> 1
| _ -> 0
let toRPN src =
let output = new ResizeArray<Token>()
let stack = new Stack<Token>()
let rec loop = function
| Num v::tokens ->
output.Add(Num v)
loop tokens
| RParen::tokens ->
stack.Push RParen
loop tokens
| LParen::tokens ->
while stack.Peek() <> RParen do
output.Add(stack.Pop())
stack.Pop() |> ignore // pop the "("
loop tokens
| op::tokens when op |> isUnary ->
stack.Push op
loop tokens
| op::tokens ->
if stack |> hasAny then
if prec(stack.Peek()) >= prec op then
output.Add(stack.Pop())
stack.Push op
loop tokens
| [] ->
output.AddRange(stack.ToArray())
output
(loop src).ToArray()
let (#) op tok =
match tok with
| Num v ->
match op with
| Sqrt -> Num (sqrt v)
| Negative -> Num (v * -1.0)
| _ -> failwith "input error"
| _ -> failwith "input error"
let (##) op toks =
match toks with
| Num v,Num u ->
match op with
| Plus -> Num(v + u)
| Minus -> Num(v - u)
| Star -> Num(v * u)
| Slash -> Num(u / v)
| Hat -> Num(u ** v)
| _ -> failwith "input error"
| _ -> failwith "inpur error"
let evalRPN src =
let stack = new Stack<Token>()
let rec loop = function
| Num v::tokens ->
stack.Push(Num v)
loop tokens
| op::tokens when op |> isUnary ->
let result = op # stack.Pop()
stack.Push result
loop tokens
| op::tokens ->
let result = op ## (stack.Pop(),stack.Pop())
stack.Push result
loop tokens
| [] -> stack
if loop src |> hasAny then
match stack.Pop() with
| Num v -> v
| _ -> failwith "input error"
else failwith "input error"
let eval input =
input |> (tokenize >> toRPN >> Array.toList >> evalRPN)
Before answering your specific question, did you notice you have another bug? Try eval "2-4" you get 2.0 instead of -2.0.
That's probably because along these lines:
match op with
| Plus -> Num(v + u)
| Minus -> Num(v - u)
| Star -> Num(v * u)
| Slash -> Num(u / v)
| Hat -> Num(u ** v)
u and v are swapped, in commutative operations you don't notice the difference, so just revert them to u -v.
Now regarding the bug you mentioned, the cause seems obvious to me, by looking at your code you missed the precedence of those unary operations:
let prec = function
| Hat -> 3
| Star | Slash -> 2
| Plus | Minus -> 1
| _ -> 0
I tried adding them this way:
let prec = function
| Negative -> 5
| Sqrt -> 4
| Hat -> 3
| Star | Slash -> 2
| Plus | Minus -> 1
| _ -> 0
And now it seems to be fine.
Edit: meh, seems I was late, Gustavo posted the answer while I was wondering about the parentheses. Oh well.
Unary operators have the wrong precedence. Add the primary case | a when isUnary a -> 4 to prec.
The names of LParen and RParen are consistently swapped throughout the code. ( maps to RParen and ) to LParen!
It runs all tests from the question properly for me, given the appropriate precedence, but I haven't checked the code for correctness.

Aggregation function - f# vs c# performance

I have a function that I use a lot and hence the performance needs to be as good as possible. It takes data from excel and then sums, averages or counts over parts of the data based on whether the data is within a certain period and whether it is a peak hour (Mo-Fr 8-20).
The data is usually around 30,000 rows and 2 columns (hourly date, value). One important feature of the data is that the date column is chronologically ordered
I have three implementations, c# with extension methods (dead slow and I m not going to show it unless somebody is interested).
Then I have this f# implementation:
let ispeak dts =
let newdts = DateTime.FromOADate dts
match newdts.DayOfWeek, newdts.Hour with
| DayOfWeek.Saturday, _ | DayOfWeek.Sunday, _ -> false
| _, h when h >= 8 && h < 20 -> true
| _ -> false
let internal isbetween a std edd =
match a with
| r when r >= std && r < edd+1. -> true
| _ -> false
[<ExcelFunction(Name="aggrF")>]
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let newd =
[0 .. (Array.length data) - 1]
|> List.map (fun i -> (data.[i], data2.[i]))
|> Seq.filter (fun (date, _) ->
let dateInRange = isbetween date std edd
match pob with
| "Peak" -> ispeak date && dateInRange
| "Offpeak" -> not(ispeak date) && dateInRange
| _ -> dateInRange)
match sac with
| 0 -> newd |> Seq.averageBy (fun (_, value) -> value)
| 2 -> newd |> Seq.sumBy (fun (_, value) -> 1.0)
| _ -> newd |> Seq.sumBy (fun (_, value) -> value)
I see two issues with this:
I need to prepare the data because both date and value are double[]
I do not utilize the knowledge that dates are chronological hence I do unnecessary iterations.
Here comes now what I would call a brute force imperative c# version:
public static bool ispeak(double dats)
{
var dts = System.DateTime.FromOADate(dats);
if (dts.DayOfWeek != DayOfWeek.Sunday & dts.DayOfWeek != DayOfWeek.Saturday & dts.Hour > 7 & dts.Hour < 20)
return true;
else
return false;
}
[ExcelFunction(Description = "Aggregates HFC/EG into average or sum over period, start date inclusive, end date exclusive")]
public static double aggrI(double[] dts, double[] vals, double std, double edd, string pob, double sumavg)
{
double accsum = 0;
int acccounter = 0;
int indicator = 0;
bool peakbool = pob.Equals("Peak", StringComparison.OrdinalIgnoreCase);
bool offpeakbool = pob.Equals("Offpeak", StringComparison.OrdinalIgnoreCase);
bool basebool = pob.Equals("Base", StringComparison.OrdinalIgnoreCase);
for (int i = 0; i < vals.Length; ++i)
{
if (dts[i] >= std && dts[i] < edd + 1)
{
indicator = 1;
if (peakbool && ispeak(dts[i]))
{
accsum += vals[i];
++acccounter;
}
else if (offpeakbool && (!ispeak(dts[i])))
{
accsum += vals[i];
++acccounter;
}
else if (basebool)
{
accsum += vals[i];
++acccounter;
}
}
else if (indicator == 1)
{
break;
}
}
if (sumavg == 0)
{
return accsum / acccounter;
}
else if (sumavg == 2)
{
return acccounter;
}
else
{
return accsum;
}
}
This is much faster (I m guessing mainly because of the exit of loop when period ended) but oviously less succinct.
My questions:
Is there a way to stop iterations in the f# Seq module for sorted series?
Is there another way to speed up the f# version?
can somebody think of an even better way of doing this?
Thanks a lot!
Update: Speed comparison
I set up a test array with hourly dates from 1/1/13-31/12/15 (roughly 30,000 rows) and corresponding values. I made 150 calls spread out over the date array and repeated this 100 times - 15000 function calls:
My csharp implementation above (with string.compare outside of loop)
1.36 secs
Matthews recursion fsharp
1.55 secs
Tomas array fsharp
1m40secs
My original fsharp
2m20secs
Obviously this is always subjective to my machine but gives an idea and people asked for it...
I also think one should keep in mind this doesnt mean recursion or for loops are always faster than array.map etc, just in this case it does a lot of unnecessary iterations as it doesnt have the early exit from iterations that the c# and the f# recursion method have
Using Array instead of List and Seq makes this about 3-4 times faster. You do not need to generate a list of indices and then map over that to lookup items in the two arrays - instead you can use Array.zip to combine the two arrays into a single one and then use Array.filter.
In general, if you want performance, then using array as your data structure will make sense (unless you have a long pipeline of things). Functions like Array.zip and Array.map can calculate the entire array size, allocate it and then do efficient imperative operation (while still looking functional from the outside).
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let newd =
Array.zip data data2
|> Array.filter (fun (date, _) ->
let dateInRange = isbetween date std edd
match pob with
| "Peak" -> ispeak date && dateInRange
| "Offpeak" -> not(ispeak date) && dateInRange
| _ -> dateInRange)
match sac with
| 0 -> newd |> Array.averageBy (fun (_, value) -> value)
| 2 -> newd |> Array.sumBy (fun (_, value) -> 1.0)
| _ -> newd |> Array.sumBy (fun (_, value) -> value)
I also changed isbetween - it can be simplified into just an expression and you can mark it inline, but that does not add that much:
let inline isbetween r std edd = r >= std && r < edd+1.
Just for completeness, I tested this with the following code (using F# Interactive):
#time
let d1 = Array.init 1000000 float
let d2 = Array.init 1000000 float
aggrF d1 d2 0.0 1000000.0 "Test" 0
The original version was about ~600ms and the new version using arrays takes between 160ms and 200ms. The version by Matthew takes about ~520ms.
Aside, I spent the last two months at BlueMountain Capital working on a time series/data frame library for F# that would make this a lot simpler. It is work in progress and also the name of the library will change, but you can find it in BlueMountain GitHub. The code would look something like this (it uses the fact that the time series is ordered and uses slicing to get the relevant part before filtering):
let ts = Series(times, values)
ts.[std .. edd] |> Series.filter (fun k _ -> not (ispeak k)) |> Series.mean
Currently, this will not be as fast as direct array operations, but I'll look into that :-).
An immediate way to speed it up would be to combine these:
[0 .. (Array.length data) - 1]
|> List.map (fun i -> (data.[i], data2.[i]))
|> Seq.filter (fun (date, _) ->
into a single list comprehension, and also as the other matthew said, do a single string comparison:
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let isValidTime = match pob with
| "Peak" -> (fun x -> ispeak x)
| "Offpeak" -> (fun x -> not(ispeak x))
| _ -> (fun _ -> true)
let data = [ for i in 0 .. (Array.length data) - 1 do
let (date, value) = (data.[i], data2.[i])
if isbetween date std edd && isValidTime date then
yield (date, value)
else
() ]
match sac with
| 0 -> data |> Seq.averageBy (fun (_, value) -> value)
| 2 -> data.Length
| _ -> data |> Seq.sumBy (fun (_, value) -> value)
Or use a tail recursive function:
let aggrF (data:float[]) (data2:float[]) std edd pob sac =
let isValidTime = match pob with
| "Peak" -> (fun x -> ispeak x)
| "Offpeak" -> (fun x -> not(ispeak x))
| _ -> (fun _ -> true)
let endDate = edd + 1.0
let rec aggr i sum count =
if i >= (Array.length data) || data.[i] >= endDate then
match sac with
| 0 -> sum / float(count)
| 2 -> float(count)
| _ -> float(sum)
else if data.[i] >= std && isValidTime data.[i] then
aggr (i + 1) (sum + data2.[i]) (count + 1)
else
aggr (i + 1) sum count
aggr 0 0.0 0

OR pattern matching

I'm trying to use an OR pattern, as described here:
let foo = function
| Some (0, x) when x > 0 | None -> "bar"
| _ -> "baz"
However, this gives a compiler error:
error FS0010: Unexpected symbol '|' in pattern matching. Expected '->'
or other token.
What am I doing wrong? Does it have to do with the when guard?
A when guard refers to a single case, regardless of how many patterns are combined. The cases need to be separated:
let foo = function
| Some (0, x) when x > 0 -> "bar"
| None -> "bar"
| _ -> "baz"
For that reason, it may be better to factor out the return value, so a possibly complex expression isn't repeated:
let foo value =
let ret = "bar"
match value with
| Some (0, x) when x > 0 -> ret
| None -> ret
| _ -> "baz"
Using an active pattern is another way to avoid such repetition:
let (|Bar|_|) = function
| Some(0, x) when x > 0 -> Some()
| None -> Some()
| _ -> None
let foo = function
| Bar -> "bar"
| _ -> "baz"
You'll need two separate match cases there because the two cases bind different sets of variables (x and nothing, respectively):
| Some(0, x) when x>0 -> "bar"
| None -> "bar"
A nice trick I sometime use when you want to guard only specific bindings of a label, in a very complex pattern, is to use my own active patterns and the & (and) pattern operator:
let (|GreaterThan|_|) lowerLimit n =
if n > lowerLimit then Some () else None
let (|LesserThan|_|) upperLimit n =
if n < upperLimit then Some () else None
let (|GreaterOETo|_|) lowerLimit n =
if n >= lowerLimit then Some () else None
let (|LesserOETo|_|) upperLimit n =
if n <= upperLimit then Some () else None
let (|InRange|_|) (lowerLimit, upperLimit) n =
if n >= lowerLimit && n <= upperLimit then Some () else None
let (|Even|Odd|) n =
if n % 2 = 0 then
Even (n / 2)
else
Odd (n / 2)
type Union =
| A of int
| B of int
| A' of int
let getSpecialCases = function
| A (Even (x & GreaterThan 4 & LesserOETo 16))
| A (Odd (x & GreaterThan 0))
| B (x & LesserOETo 0)
| A' (Even (x & InRange (5, 16)))
| A' (Odd (x & GreaterThan 0)) -> Some x
| _ -> None
And of course you can just make a function to active pattern wrapper:
let (|P|_|) pred x =
if pred x then Some () else None
let ``match`` = function
| Even (x & pred (fun x -> x >= 7 && x <= 54)) -> Some x
| _ -> None

Resources