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)); }
Result
s 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); _studentRepository.Save(student); }
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.
--Vlad
https://enterprisecraftsmanship.com
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 »