Skip to content

Improve performance for pattern matches with null and empty strings #19177

@fkj

Description

@fkj

Is your feature request related to a problem? Please describe.

I often have to check for null and empty strings for interoperability with C# code that handles user-provided input.

What I intuitively write is usually something like:

let a (x: string) =
    match x with
    | null
    | "" -> "a"
    | _ -> "b"

or, if the empty string is a meaningful value, something like:

let a (x: string) =
    match x with
    | null -> "a"
    | "" -> "b"
    | _ -> "c"

The code above is simplified for clarity, and in real life the function would of course actually do something instead of just returning a string.
Often, I would also add additional cases to the pattern match, for instance when parsing input that may be null.

A similar intuitive pattern is "pure" empty string checking:

let a (x: string) =
    match x with
    | "" -> "a"
    | _ -> "b"

The compiler does not generate performant IL code for these patterns because it ends up using System.String::Equals instead of either System.String::IsNullOrEmpty or a combination of System.String::get_Length and brfalse.s for null checking.

This leads to a performance penalty as described in e.g. .NET code quality rule CA1820.
It also leads to static analysis tools like Ionide's EmptyStringAnalyzer giving confusing seemingly false (but actually true) positives on pattern matching code where the user is unwittingly using System.String::Equals.

Describe the solution you'd like

I would like the compiler to, whenever possible, generate IL that uses the more efficient implementations instead of System.String::Equals.

For instance, the pattern match:

let a (x: string) =
    match x with
    | null
    | "" -> "a"
    | _ -> "b"

should generate a use of System.String::IsNullOrEmpty instead of generating a use of System.String::Equals.

Describe alternatives you've considered

Current workarounds include:

  1. Always using guarded pattern matching with an appropriate combination of isNull, IsNullOrEmpty and/or String.Length = 0 instead of directly pattern matching on null and "". This is many more keystrokes and less readable.
  2. Always using an if statement to handle null/empty string checking, then doing any other pattern matching inside the appropriate case. This splits the control flow into two parts with many more keystrokes, making the code less readable.

The main drawback of these two workarounds is that they produce code that looks "wrong" and is very susceptible to future developers simplifying it because they don't realize the performance penalty exists.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions