How to refactor the boolean logic of this F# function? I have a list of path I want to filter out. I want to keep only paths matching a specific pattern and remove all paths matching another specific pattern.
To achieve this I pattern matched on each pattern. Here is the code:
let stringToSpan (s: string) =
    System.ReadOnlySpan&lt;char&gt;(s.ToCharArray())

let matchWildcard pattern text =
    System.IO.Enumeration.FileSystemName.MatchesSimpleExpression(stringToSpan pattern, stringToSpan text)

let filterByPattern (includePattern: string Option) (excludePattern: string Option) (paths: string seq) :string seq =
    paths
    |&gt; Seq.filter
        (fun path -&gt;
            let shouldKeep =
                match includePattern with
                | Some pattern -&gt; matchWildcard pattern path
                | None -&gt; true

            if not shouldKeep then
                false
            else
                match excludePattern with
                | Some pattern -&gt; matchWildcard pattern path |&gt; not
                | None -&gt; true)

You can try this snippet with:
seq {&quot;test&quot;; &quot;foo/bar&quot;; &quot;foo/toto&quot;}
|&gt; filterByPattern (Some &quot;foo/*&quot;) (Some &quot;*toto*&quot;)
|&gt; printfn &quot;%A&quot;

// output: foo/bar

If it matters, this snippet is used inside a dotnet tool to clone multiple git repositories. You can find this tool on GitHub.
I think that we could improve this snippet a bit. Can we?
I would avoid using Option .IsNone and .Value, mainly because .Value will throw an exception if you made a mistake and called it at the wrong time. Almost all F# code will avoid that property and use pattern matching instead or some other function.
Here's another way to write the patternPredicate function, which I renamed to includePath do describe what it actually does:
let includePath (includePattern: string option) (excludePattern: string option) path =
    let isIncluded =
        match includePattern with
        | Some includePattern -&gt; matchWildcard includePattern path
        | None -&gt; true
    let isExcluded =
        lazy
            match excludePattern with
            | Some excludePattern -&gt; matchWildcard excludePattern path
            | None -&gt; false
    isIncluded &amp;&amp; not isExcluded.Value

My approach here was to first build up the values isIncluded and isExcluded so that we can write isIncluded &amp;&amp; not isExcluded.Value. This breaks the problem down into understandable and explicit parts.
Note the use of a lazy value for isExcluded. This means that it won't be evaluated unless isIncluded is true, so we don't need to call matchWildcard unnecessarily.