Simplify if null by using an Option

We start off with the following code: (Note that some types have been omitted for brevity)

#nullable enable

using System;

public class Example
{
    public VersionEnvironment? GetCurrentVersionEnvironment(PackageName packageName)
    {
        var currentVersion = ReadCurrentVersion(packageName);

        if (currentVersion is null)
        {
            return null;
        }

        var versionPath = GetVersionPath(packageName, currentVersion);
        return new VersionEnvironment(currentVersion, versionPath);
    }

    public PackageVersion? ReadCurrentVersion(PackageName name) => null; // Real implementation omitted

    public string GetVersionPath(PackageName name, PackageVersion version) => null!; // Real implementation omitted
}

The function GetCurrentVersionEnvironment doesn't do much, but it's not pleasant to look at, because of that early return condition.

Step Ⅰ

We start off by changing the return type of ReadCurrentVersion from PackageVersion? to an Option:

public Option<PackageVersion> ReadCurrentVersion(PackageName name) => Option.None<PackageVersion>(); // Real implementation omitted

Step Ⅱ

This immediately breaks the GetCurrentVersionEnvironment, because our null check no longer makes sense. Instead of checking for null and returning early, we can use Select on the Option to project its value.

public Option<VersionEnvironment> GetCurrentVersionEnvironment(PackageName packageName)
{
    return ReadCurrentVersion(packageName)
        .Select(currentVersion => {
            var versionPath = GetVersionPath(packageName, currentVersion);
            return new VersionEnvironment(currentVersion, versionPath);
        });
}

Step Ⅲ

This is already much simpler, since we've got rid of that explicit null check.

There's still room for simplification, since our projection takes up two lines and we ideally only want a single expression in our projection.

We can achieve this by translating our expression to query syntax:

public Option<VersionEnvironment> GetCurrentVersionEnvironment(PackageName packageName)
{
    return from currentVersion in ReadCurrentVersion(packageName)
           let versionPath = GetVersionPath(packageName, currentVersion)
           select new VersionEnvironment(currentVersion, versionPath);
}

Step Ⅳ

Since our method now consists of only one expression, we can make it an expression body:

public Option<VersionEnvironment> GetCurrentVersionEnvironment(PackageName packageName)
    => return from currentVersion in ReadCurrentVersion(packageName)
              let versionPath = GetVersionPath(packageName, currentVersion)
              select new VersionEnvironment(currentVersion, versionPath);