Skip to content

Real Life examples of Either in C#

Following my first article and the Dev Adventures .NET API Template, lots of people started asking for examples of functional programming, and namely the Option/Either monad in C#. The concept seems interesting to most, but it’s hard for a person used to programming in imperative style to understand its real life value right away.

Hoping to make things easier, I will be showing 3 examples: parsing an imaginary excel file; performing an HTTP request; implementing login functionality.

We will start by looking at the most common approaches for solving these problems, what issues arise, and how to solve them using functional programming.

Problem 1: Parsing an Excel file

If you’re used to programming in imperative style and arrive at the problem of parsing a list of data from an Excel file, you will most likely end up with a similar function definition.

RowData ParseRow(ExcelRow row);

Takes an excel row and returns the parsed data. Clean, simple and straightforward, right?

Well, no.

There are a few issues with this function definition.

You don’t know what’s going to happen if it fails to parse the given row.

Does it throw an exception or return null? Well, you have no way of knowing that except by trial and error.

Code is about communication, and this definition communicates almost nothing meaningful to the caller.

You have no way of aggregating the errors

Often, when parsing Excel files you will also need to be able to generate a report on the parsing results.

The function may throw meaningful exceptions with descriptive error messages, but unless you wrap it in an [ugly] helper function, you have no way of aggregating these errors.

There is also the possibility of adding an Errors property to the RowData class or wrapping the result in a ParsingResult class. But this is either screwing with the single responsibility principle or over-complicating the operation.

How FP comes to the rescue

Well, if we start thinking about our row parsing function in plain English, we will arrive at a definition similar to: “a function either returning the parsed row data, or an error”.

Fortunately, FP provides us with a mechanism allowing us to return exactly that.

// Option<T, TException> is also called Either
Option<RowData, ParsingError> ParseRow(ExcelRow row);

Defining our function like that solves all of the aforementioned issues. An example use-case would look like:

var parsingResults = excelReport

var rows = parsingResults.Values();
var errors = parsingResults.Exceptions();

See how easy it is to aggregate the errors now? Beautiful. 🙂

Problem 2: Performing HTTP requests

If you’re a developer with more than 1 days of experience, chances are that at some point in your career you had to make an HTTP request.

Let’s say you’ve taken up on C# with .NET and you Google something along the lines of C# make http request. Most likely one of the first results you’ll see is a link to MSDN with a very detailed description of the HttpClient class and quite a few examples.

Let’s look at one of them. A function to post a given Product to an external service.

// This is taken straight out of MSDN
async Task<Uri> CreateProductAsync(Product product)
   // The client variable is an HttpClient 
   HttpResponseMessage response = await client.PostAsJsonAsync(
       "api/products", product);

   // return URI of the created resource.
   return response.Headers.Location;

Now, this may appear as a decent example of a real world application of the HttpClient class, but I would argue that there are quite a few issues with it.

  1. The caller of PostAsJsonAsync is supposed to know to call EnsureSuccessStatusCode. If he forgot to do that and called ReadAsAsync, for example, he would’ve gotten an exception.

  2. EnsureSuccessStatusCode itself throws an exception, which is weird, considering it is a method designed to help you avoid exceptions.

  3. Even if everything with the request went well, there is no guarantee that the response will contain a proper Location header, therefore there is no guarantee that the Uri you’ll be getting will not be null.

So in order for the consumer of CreateProductAsync to be safe, he should guard himself with at least one catch block and a null check.

Not very consumer friendly.

How FP comes to the rescue

Communicating with external services is a dangerous operation that will more often fail than succeed.

The nature of communicating through HTTP is error-prone itself and we should be planning for that.

The Either monad is great for situations where we perform operations that are likely to fail. Let’s look at a PostAsync function definition employing Either.

Task<Option<HttpResponseMessage, RemoteError>> PostAsync<TModel>(string route, TModel model)

The nature of the Option/Either type itself “forces” the caller to handle both the success and error case in an elegant way and doesn’t require him to remember anything (like calling EnsureSuccessStatusCode).

Let’s look at the CreateProductAsync example once more. This time using our new PostAsync function.

async Task<Option<Uri, RemoteError>> CreateProductAsync(Product product)
   var response = await PostAsync<Product>("api/products", product);

   return response.Match(
       some: res =>
           var locationHeader = res.Headers?.Location;

           return locationHeader
               .SomeNotNull("The response did not contain a location header.");
       none: err => Option.None<Uri, RemoteError>(err));

The implementation did not get bloated or become unreadable, but all of the aforementioned issues are now resolved.

  1. The caller of PostAsync is not required to remember to call EnsureSucccessStatusCode.
  2. The caller of PostAsync will never get an exception and is not required to “protect” himself with a catch block.
  3. If the request passed, but the response did not contain a proper Location header, the caller will not be getting a null value, but a descriptive error message.

And all of this without a single if statement.

Problem 3: Implementing login functionality.

For this problem we’ll be going through 4 stages of a Login function that is supposed to generate a JWT. Starting from a very naive, but quite prevalent approach, to an approach employing the Either monad. The benefits will be getting more and more apparent.

An example you’ll see often is something along the lines of.

public string Login(string username, string password)
   return null;
 if(!IsValid(username, password))
   return null;
 var user = GetUser(username);

 ... generate JWT

There is some validation, but the caller will never know exactly what went wrong. He is also expected to know to check for a null value. This is a very limiting, error-prone, but sadly frequent approach.

It’s obviously not good enough, so we decide to step it up a notch and start throwing some descriptive exceptions.

public string Login(string username, string password)
   throw new UserNotFoundException();
 if(!IsValid(username, password))
   throw new InvalidCredentialsException();
 var user = GetUser(username);

 ... generate JWT

Much better, right?

Well, no.

Yes, the caller will know why the function has failed, but if this were a closed source project and the function was undocumented, he would never know what and when to catch.

On top of that, cases in which the user will not exist or invalid credentials will be provided are not exceptional at all. I haven’t seen any statistics, but I would take a bet that a login form is more often populated with incorrect information rather than with correct information. Throwing exceptions is very expensive (in fact, the most expensive operation in .NET) therefore we shouldn’t be doing it if we’re expecting our function to fail more often than succeed.

We are also required to implement new exception classes for every error, which will soon turn into a 1000 .cs files nightmare.

This starts to seem harder than we thought. We get frustrated and organize a brainstorming session with a few colleagues. Eventually we arrive at the LoginResult class. A sophisticated approach that solves all of our problems. We will be communicating clearly with the caller and avoid taking a performance hit by throwing exceptions often. The errors will be contained in an Errors property and the JWT in a Jwt one.

public LoginResult Login(string username, string password)
   return Error("User doesn't exist.");
 if(!IsValid(username, password))
   return Error("Invalid credentials.");
  var user = GetUser(username);

 ... generate JWT

LoginResult Error(params string[] errors) =>
   new LoginResult { Errors = errors };


Or not really.

Yes, the LoginResult class makes things much more obvious, but the caller is still supposed to know to check for errors and pollute his code with statements like

if (result.Errors?.Any() == true)


if (!string.IsNullOrEmpty(result.Jwt))

If he forgets to do that, he’ll get a runtime exception, which shouldn’t really happen in a production ready system.

Also, wrapping every result into a Result class could very easily turn into a huge inheritance hierarchy that is hard to understand and maintain.

How FP comes to the rescue

It doesn’t take much thinking to realize that our Login function should either generate a JWT or return an error. These two scenarios should not be mixed together.

So we end up with this definition:

Option<Jwt, Error> Login(string username, string password)

Now that the results are decoupled, we end up with much less bloat and much more possibilities.

public Option<Jwt, Error> Login(string username, string password)
     return Error("User does not exist.");
   if (!IsValid(username, password))
     return Error("Invalid credentials.")

   var user = GetUser(username);

   ... generate JWT

Option<Jwt, Error> Error(params string[] errors) =>
     Option.None<Jwt, Error>(new Error(errors));

For example, the Error that your service layer (the “home” of the Login function) returns can have a “type”, which can later be mapped to a proper HTTP code from your ASP.NET Controller while maintaining cohesion.

Also, the consumer will not be required to pollute his code with any if statements, as the compiler will “force” him to handle both the error and the success scenario.

Putting it all together

I hope that the benefits of using Either became obvious. I also want to note that Either is not meant to replace the traditional exception mechanism entirely. There are cases in which the best thing you can do is to throw an exception. Out of memory, thread aborted, network is down. All of these are very good examples in which there is absolutely nothing you can do and you seriously need to abort the operation.

But be explicit about operations you know are expected to fail. Don’t be a jerk to the consumers of the code you produce, but communicate clearly. After all, code is for humans.

Following the principles in this article I have made a .NET Core multi project template that is available in the VS Marketplace.

You can read more about it here.

As always, don’t hesitate to contact me if you have any feedback, whether positive or negative. I’m always trying to improve the given examples.

Have fun.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *