Today, we’ll talk about the discussion I had with regards to the small open source library I maintain: C# functional extensions. The discussion was about whether we should enable the conversion from the Result class to Maybe.

1. Result and Maybe

First off, what are Result and Maybe?

Result is a class that represents a result of an operation. For example, when you are trying to parse an email, you may implement this method like this:

// Email class
public static Result<Email> Create(string input)
    if (string.IsNullOrWhiteSpace(input))
        return Result.Failure("Value is required");

    string email = input.Trim();

    if (email.Length > 150)
        return Result.Failure("Invalid length");

    if (Regex.IsMatch(email, @"^(.+)@(.+)$") == false)
        return Result.Failure("Invalid email");

    return Result.Success(new Email(email));

Results are used for validation as an alternative to throwing exceptions.

Result is better for validation because, unlike exceptions, it is explicit and doesn’t create new pathways in your code that you have to inspect separately from the regular method outputs.

The consumption of a Result looks like this:

public IActionResult Register(RegisterRequest request)
    Result<Email> email = Email.Create(request.Email);
    if (email.IsFailure)
        return BadRequest(email.Error);

    var student = new Student(email.Value);

Maybe looks similar but has a different purpose: it is an alternative to nullable reference types in C#. It helps you to explicitly represent a no-value situation in code:

public class StudentRepository
    public Maybe<Student> GetById(long studentId)
        /* ... */

Here, we are explicitly telling the client of the GetById method that this method may not find a student for the given id.

Note that both Result and Maybe are written such that if you try to access .Value when there’s none, you get an exception. This is done this way to adhere to the Fail Fast principle: you shouldn’t assume the value is there when it’s not.

Result<Email> email = Email.Create(request.Email);
Maybe<Student> student = _repo.GetById(request.Id);

// Both email.Value and student.Value throw if no value

2. Conversion between Result and Maybe

Now, the question is: should we be able to convert from Result to Maybe?

Especially given that they look quite similar. The only difference between the two is that there’s an additional Error field in Result.

In theory, we could introduce an implicit conversion operator that would work like this:

Result<Email> result = Email.Create(request.Email);
Maybe<Email> maybe = result; // Implicit conversion

Such an implicit conversion is a bad idea, though.

This conversion leads to the loss of information: the error that caused the failure is now lost. You want your code to explicitly check for errors in the result, you don’t want that result to silently convert to a Maybe.

This is the same guideline .NET applies in its BCL library:

int myInteger = 0;
double myDouble = 0;

myInteger = myDouble; // Doesn't compile
myDouble = myInteger; // Compiles

The implicit conversion from double to int isn’t allowed because it also leads to the loss of information: integers can’t hold fractions, only whole values.

The conversion from double to int must be an explicit choice, such that you don’t lose that information accidentally:

myInteger = (int)myDouble; // Now it compiles

Same with the conversion from Result to Maybe. It should be an explicit choice, so that it’s clear you made that choice consciously.

In the library, such an explicit choice can be made using the GetValueOrDefault method:

Result<Email> result = Email.Create(request.Email);
Maybe<Email> maybe = result.GetValueOrDefault();

Similar to (int)myDouble, the above code makes it clear that you do want to make this conversion, and didn’t just change the type of the variable accidentally.


Enjoy this message? Here are more things you might like:

Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »