EasyMonads 2.2.0

dotnet add package EasyMonads --version 2.2.0                
NuGet\Install-Package EasyMonads -Version 2.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EasyMonads" Version="2.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add EasyMonads --version 2.2.0                
#r "nuget: EasyMonads, 2.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install EasyMonads as a Cake Addin
#addin nuget:?package=EasyMonads&version=2.2.0

// Install EasyMonads as a Cake Tool
#tool nuget:?package=EasyMonads&version=2.2.0                

Support for just a few Monad types in C#

Key differences from other libraries

No exceptions and no 'unsafe' methods when working with null values

Some libraries go out of their way to stop you from providing null as a value when creating an instance of a monad or acting on an existing monad. For example, trying to instantiate a monad with a null value will throw an exception. So will returning null from a Match statement. The "work-around" in these situations is using methods with "Unsafe" in the name, as if null is an implicitly hazardous thing.

Instead of throwing runtime exceptions or asking you to use methods with a different naming convention*, this library will try to let you do what you want and continue running.

* There are some method names in this library that include the word "Nullable". The behavior of these methods is the same as their standard counterparts. The reason for these "Nullable" alternatives is to support projects with <nullable> enabled; you may provide nullable items to these methods without nullability warnings.

The monads supported by this library all have some default state:

  • Maybe<T> ⇒ None
  • Either<TLeft, TRight> ⇒ Neither

Rather than throw an exception when receiving null values, the monads you get back will just be in their default states. These states should already be handled by the caller - there is nothing "unsafe" about it. Especially when compared to throwing an exception which may terminate the program.

Similarly, if you want to return null in your Match statements, why shouldn't you? This library is not going to get in the way of writing the code you want to write.

Use the same types for async

While implementing the AsyncExtensions for Either and Maybe, I discovered Task<Either<TLeft, TRight>> and Task<Maybe<T>>> work perfectly fine. Implementing distinct EitherAsync and MaybeAsync types is not necessary in order to add support for the Linq query syntax.

It is only when you add Linq query support for Task<T> that you begin to encounter ambiguity problems between Task<Monad> and Task<T>. I do not intend to add Linq query support for Task<T>, so this should not become a problem.

Monad Types

Maybe<T>

A simple wrapper around a value which may/not exist. A good alternative to nullable types.

Maybe<int> daysSinceLastAccident = 10;

daysSinceLastAccident.IfNone(
   () => Console.WriteLine($"An accident has never been reported!"));

daysSinceLastAccident.IfSome(
   x => Console.WriteLine($"The last accident occurred {x} days ago!"));

int nonNullableDays = daysSinceLastAccident.GetSomeOrDefault(0);

double daysDouble = daysSinceLastAccident.Match(
   () => 0.0,
   x => double.Parse(x));

Either<TLeft, TRight>

A choice monad with left (bad) and right (good) states/values. There is also a neither (empty) state which does not contain any value, sort of like an implicit Maybe<Either<TLeft, TRight>>.

A great example of when to use this is when deserializing a response from a web API. Consider an API that may either return some good data with a 200 response or a standard error message with a 4xx or 5xx response. You already know which data type to deserialize to by looking at the HTTP response, but what kind of object can you use to represent two different types of data? One option could be object or dynamic, but those become difficult to work with almost immediately after you implement them.

A better option is deserializing to the correct data type, then storing the instance in an Either.


Either<int, MyDTO> apiResponse = httpStatus == 200
   ? JsonSerializer.Deserialize<MyDTO>(responseData)
   : JsonSerializer.Deserialize<int>(responseData);

eitherApiResponse.DoRight(dto =>
{
   // Do something with the dto data
});

eitherApiResponse.DoLeftOrNeither(
   left: errorCode =>
   {
      Console.WriteLine($"The API responded with an error code: {errorCode}");
   },
   neither: () => Console.WriteLine($"The API did not respond"))

bool wasTheRequestSuccessful = eitherApiResponse.IsRight;

Maybe<MyDTO> apiResponse = eitherApiResponse.ToMaybe();

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.0 2,621 4/6/2024
2.1.0 1,419 2/13/2024
2.0.0 2,862 1/7/2024
1.4.3 3,876 6/26/2023
1.4.2 154 6/26/2023
1.3.0 8,486 1/13/2023
1.2.0 2,049 8/16/2022