Nullable Reference Types and Either Pattern

Nullable Reference Types and Either Pattern

nullable reference types in c#
nullable reference types operator
the annotation for nullable reference types
automapper nullable reference types
nullable reference types .net 5
c# nullable
dereference of a possibly null reference
c# 8 enable</nullable reference types

I'm trying out the new Nullable Reference Types in C# 8.0, and I'm facing the following issue.

Given this struct:

public readonly struct Either<TReturn, TError>
    where TReturn : struct
    where TError : struct
{
    public TError? Error { get; }
    public TReturn? Response { get; }

    public Either(TError? error, TReturn? response)
    {
        if (error == null && response == null)
        {
           throw new ArgumentException("One argument needs not to be null.");
        }
        if (error != null && response != null)
        {
            throw new ArgumentException("One argument must be null.");
        }
        Error = error;
        Response = response;
    }
}

How can I tell the compiler that either Error or Response is not null, and that they can't both be null? Is there a way to do such a thing with the new attributes?


Update for structs

The code doesn't change when the result types change to structs. To use struct type parameters, the following constraints have to be added to the interface and the types:

where TResult : struct
where TError  : struct

When I think about the Either pattern, I think about F#, pattern matching and discriminated unions, not nulls. In fact, Either is a way to avoid nulls. In fact, the question's code looks like an attempt to create a Result type, not just an Either. Scott Wlaschin's Railway Oriented Programming shows how such a type can be used to implement error handling in a functional language.

In F#, the Result type is defined as:

type Result<'T,'TError> = 
    | Ok of ResultValue:'T 
    | Error of ErrorValue:'TError

We can't do that in C# 8 yet, because there are no discriminated unions. Those are planned for C# 9.

Pattern Matching

What we can do, is use pattern matching to get the same behavior eg:

interface IResult<TResult,TError>{} //No need for an actual implementation

public class Success<TResult,TError>:IResult<TResult,TError>

{
    public TResult Result {get;}

    public Success(TResult result) { Result=result;}
}

public class Error<TResult,TError>:IResult<TResult,TError>
{
    public TError ErrorValue {get;}

    public Error(TError error) { ErrorValue=error;}
}

This way there's no way to create an IResult<> that is both a success and error. This can be used with pattern matching, eg:

IResult<int,string> someResult=.....;

if(someResult is Success<int,string> s)
{
    //Use s.Result here
}

Simplifying the expressions

Given C# 8's property patterns, this could be rewritten as :

if(someResult is Success<int,string> {Result: var result} )
{
    Console.WriteLine(result);
}

or, using switch expressions, a typical railway-style call :

IResult<int,string> DoubleIt(IResult<int,string> data)
{
    return data switch {    Error<int,string> e=>e,
                            Success<int,string> {Result: var result}=>
                                       new Success<int,string>(result*2),
                            _ => throw new Exception("Unexpected type!")
                            };
}    

F# wouldn't need that throw as there's no way that an Result<'T,'TError> would be something other than Ok or Error. In C#, we don't have that feature yet.

The switch expression allows exhaustive matching. I think the compiler will generate a warning if the default clause is missing too.

With deconstructors

The expressions can be simplified a bit more if the types have deconstructors, eg:

public class Success<TResult,TError>:IResult<TResult,TError>
{
    public TResult Result {get;}

    public Success(TResult result) { Result=result;}

    public void Deconstruct(out TResult result) { result=Result;}
}

public class Error<TResult,TError>:IResult<TResult,TError>
{
    public TError ErrorValue {get;}

    public Error(TError error) { ErrorValue=error;}

    public void Deconstruct(out TError error) { error=ErrorValue;}
}

In that case the expression can be written as :

return data switch {    
                Error<int,string> e => e,
                Success<int,string> (var result) => new Success<int,string>(result*3),
                _ => throw new Exception("Unexpected type!")
};

Nullability

The question started with nullable reference types, so what about nullability? Will we get a warning in C# 8 if we try to pass a nulll?

Yes, as long as NRTs are enabled. This code :

#nullable enable

void Main()
{
     IResult<string,string> data=new Success<string,string>(null);
     var it=Append1(data);
     Console.WriteLine(it);
}

IResult<string,string> Append1(IResult<string,string> data)
{
    return data switch {    Error<string,string> e=>e,
                            Success<string,string> (var result)=>
                                new Success<string,string>(result+"1"),
                            _ => throw new Exception("Unexpected type!")
                            };
}

Genereates CS8625: Cannot convert null literal to non-nullable reference type

Trying

string? s=null;
IResult<string,string> data=new Success<string,string>(s);

Generates CS8604: Possible null reference argument ....

Essential .NET, Here are some of the reasons why nullable reference types are less than ideal: (Not to mention whether analysis would include checking of all library APIs that are (the latter opens up some intriguing factory pattern implementations). Nullable reference types. 04/21/2020; 7 minutes to read +6; In this article. C# 8.0 introduces nullable reference types and non-nullable reference types that enable you to make important statements about the properties for reference type variables: A reference isn't supposed to be null. When variables aren't supposed to be null, the compiler


When implementing Either monad, you should use two different constructors. That way, you can easily get away from those checks because your implementation will make sure that there is no way to have both properties assigned at the same time.

public readonly class Either<TReturn, TError>
{
    bool _successful;
    private TError _error { get; }
    private TReturn _response { get; }

    public Either(TError error)
    {
        _error = error;
    }

    public Either(TReturn response)
    {
       _successful = true;
       _response = response;
    }
}

Besides that, you need to add a method (to the struct) which will be used to extract the value from the struct, and transform it into common return type:

public Match<T>(Func<TError, T> errorFunc, Func<TResponse, T> successFunc)
    => _successful ? successFunc(_response) : errorFunc(_error);

That way, you are enforcing users to handle both cases (success, error) and provide functions which will do transformation into common type:

var errorEither = new Either<string, int>(10); // example of error code
var successEither = new Either<string, int>("success"); // example of success

var commonValueError = errorEither.Match<bool>(err => false, succ => true);
var commonValueSuccess = successEither.Match<bool>(err => false, succ => true);

Design with nullable reference types, NET, including either Visual Studio or the .NET Core CLI. Incorporate nullable reference types into your designs. In this tutorial, you'll build a  These types will make use of both nullable and non-nullable reference types to express which members are required and which members are optional. Nullable reference types communicate that design intent clearly: The questions that are part of the survey can never be null: It makes no sense to ask an empty question. The respondents can never be null.


You can do something like this with Resharper ContractAnnotation. It's not C# 8 specific, (but... I don't think your example is really using nullable reference types anyway, is it? You are using nullable structs.)

[ContractAnnotation(
   "=> pError: null, pResponse: notnull; => pError: notnull, pResponse: null"
)]
public void Get(out TError? pError, out TReturn? pResponse) {
   pError = Error;
   pResponse = Response;
}

(The meaning of the string is that left of the => is input conditions, and right of the => is output conditions, with ; separating different cases, and an unlabled value referring to the method return value. So in this case: regardless of input, the output condition is ether of null/notnull or notnull/null.)

Then use C# 7 feature out var:

GetAnEitherFromSomewhere()
.Get(out var error, out var response);
if (error != null) {
   // handle error
   return;
}

// response is now known to be not null, because we can only get here if error was null

Honestly I'm finding JetBrains's annotations [NotNull], [CanBeNull], and [ContractAnnotation] to be a lot more flexible (though more verbose) than nullable reference types. Basically they allow for the intermediate case, where there are situations where the value can be null, but also situations where the value cannot be null, and those situations are distinguishable at run-time. With nullable reference types, I can't specify the in-between case, I have to choose either definitely nullable or definitely not nullable.

Even something common like TryParse:

// should the result be nullable with nullable reference types on?
// you have to either lie with ! or else use ? and assume it can always be null
public bool TryParse(string pString, out SomeClass pResult) {
   if (<cant parse>) {
      pResult = null;
      return false;
   }

   pResult = value;
   return true;
}

// works great with JetBrains annotations and nullable reference types off
// now you can know that the result is null or notnull
// based on testing the bool return value
[ContractAnnotation("=> true, pResult: notnull; => false, pResult: null")]
public bool TryParse(string pString, out SomeClass pResult) {
   ...
}

C# 8 nullable reference types, Explore how C# 8 lets us express whether a variable shouldn't be null, and when it can be null. We don't want to make Name nullable as then we need to place traditional null checks everywhere (which is against the purpose of non-nullable reference types) We can't create a constructor to enforce the MyOptions class to be created with a non-nullable name value as the Configure method construct the options instance for us


Nullable Reference Types: Contexts and Attributes – A Look at New , With the introduction of Nullable Reference Types (NRTs), the C# type to be of either value or nullable reference type), we will likely use the  If you want to go deeper, please visit the docs on Nullable Reference Types, or check some of the earlier posts on the topic (Take C# 8.0 for a spin, Introducing Nullable Reference Types in C#). There are many more nuances to how you can tune your nullable annotations, and we use a good many of them in our “nullification” of the .NET Core


Nullable Reference Types: Migrating a Codebase – A Look at New , Before we jump into nullable reference types, here is a quick Switch Expressions and Pattern-Based Usings · Recursive Pattern Matching · Async to jump to the next issue in either the current file or the complete solution. One popular approach instead uses option types to express that a value is either None or Some(T) for a given reference type T. Any access to the T value itself is then protected behind a pattern matching operation to see if it is there: The developer is forced, in essence, to “do a null check” before they can get at the value and start


C# 8.0 Nullable Reference types are here!, One of the most important is the support of nullable reference types (NRT). Either create a new application that runs on .net core 3.0 or open  Try out Nullable Reference Types With the release of .NET Core 3.0 Preview 7, C# 8.0 is considered "feature complete". That means that the biggest feature of them all, Nullable Reference Types, is also locked down behavior-wise for the .NET Core release.