Pure-Monads 1.0.4

There is a newer version of this package available.
See the version list below for details.
dotnet add package Pure-Monads --version 1.0.4                
NuGet\Install-Package Pure-Monads -Version 1.0.4                
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="Pure-Monads" Version="1.0.4" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Pure-Monads --version 1.0.4                
#r "nuget: Pure-Monads, 1.0.4"                
#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 Pure-Monads as a Cake Addin
#addin nuget:?package=Pure-Monads&version=1.0.4

// Install Pure-Monads as a Cake Tool
#tool nuget:?package=Pure-Monads&version=1.0.4                

Pure Monads

NuGet Version

Contains implementations of the most commonly used monads:

  • Option
  • Result
  • Either

In addition contains Pipe extensions for chaining function calls.

Examples

Option

Option monad represents 2 possible states: a value or no value.

using static Option;

// Option instances can be created:
var some1 = Option<int>.Some(1);        // via factory method
var some2 = 2.Some();                   // via extension method
Option<int> some3 = 3;                  // by implicit conversion

var none1 = Option<int>.None();         // via factory method
var none2 = None<int>();                // via factory method in non-generic static Option class

// Standard monad operations:
var mapResult = some1.Map(value => value + 10);                     // == Some(11)
var flatMapResult = some2.FlatMap(value => (value + 10).Some());    // == Some(12)

// Matching by invoking corresponding function:
var matchResult = none1.Match(value => $"Some {value}!", () => "None!");   // == "None!"

// Matching by invoking corresponding action: 
void PrintSome<TValue>(TValue value) => Console.WriteLine($"Some: {value}!");
void PrintNone() => Console.WriteLine($"None!");

some1.On(PrintSome, PrintNone);         // Prints "Some: 1!"
some1.OnSome(PrintSome);                // Prints "Some: 1!"
some1.OnNone(PrintNone);                // Prints nothing
none1.OnNone(PrintNone);                // Prints "None!"

// Converting a nullable value into an Option instance:
var notNull = "I'm not null!";
var someFromNotNull = notNull.NullAsNone();   // == Some("I'm not null!")

string? itIsNull = null;
var noneFromNull = itIsNull.NullAsNone();     // == None

// Providing alternative values for None:
int alt1 = none1.Or(100);                         // Directly passed alternative value
int alt2 = none1.Or(() => 100);                   // Alternative value retrieved from a delegate
Option<int> alt3 = none1.Or(100.Some());          // Directly passed alternative monad
Option<int> alt4 = none1.Or(() => 100.Some());    // Alternative monad retrieved from a delegate

// Extracting a value from Some or throwing an exception:
try
{
    none1.ValueOrFailure();     // Throws an exception
}
catch {}

// Returning a Some value or None from dictionary:
var dict = new Dictionary<int, string>
{
    { 1, "One" },
    { 2, "Two" },
};

var dictValue1 = dict.GetOrNone(1);   // == Some("One")
var dictValue3 = dict.GetOrNone(3);   // == None

Result

Result monad represents either a value or an error.

The following code samples are based on Result<TValue, TError> type. Another version with identical methods exists having TError type defaulted to Exception: Result<TValue>.

using static Result;

// Result instances can be created:
var value1 = Result<int, string>.Value(1);           // via factory method
var value2 = Value<int, string>(2);                  // via factory method in non-generic static Result class
Result<int, string> value3 = 3;                      // by implicit conversion

var error1 = Result<int, string>.Error("Error 1");   // via factory method
var error2 = Error<int, string>("Error 2");          // via factory method in non-generic static Result class
Result<int, string> error3 = "Error 3";              // by implicit conversion

// Standard monad operations:
var mapResult = value1.Map(value => value + 10);                              // == Value(11)
var flatMapResult = value2.FlatMap(value => Value<int, string>(value + 10));  // == Value(12)

// Matching by invoking corresponding function:
var matchResult = error1.Match(     // == "Error: Error 1"
    value => $"Value: {value}",
    err => $"Error: {err}");

// Matching by invoking corresponding action: 
void PrintValue<TValue>(TValue value) => Console.WriteLine($"Value: {value}");
void PrintError<TError>(TError err) => Console.WriteLine($"Error: {err}");

value1.On(PrintValue, PrintError);       // Prints "Value: 1"
value1.OnValue(PrintValue);              // Prints "Value: 1"
value1.OnError(PrintError);              // Prints nothing
error1.OnError(PrintError);              // Prints "Error: Error 1"

// Extracting a value or an error as Option<TValue> or Option<TError>:
var someValue1 = value1.Value();     // == Some(1)
var noneError1 = value1.Error();     // == None

var noneValue2 = error2.Value();     // == None
var someError2 = error2.Error();     // == Some("Error 2")

// Result has methods to safely invoke delegates:
var result1 = Result.From(() => "Value 1");   // == Value("Value 1)"
var result2 = Result.From(() =>               // == Error(Exception)
{
    throw new Exception("Error 1");
    return "Value 1";
});

// Async versions of the methods also exist:
var result3 = await Result.FromAsync(async () =>    // == Value("Value 1")
{
    await Task.CompletedTask;
    return "Value 1";
});
var result4 = await Result.FromAsync(async () =>    // == Error(Exception)
{
    await Task.CompletedTask;
    
    throw new Exception("Error 1");
    return "Value 1";
});

Either

Either monad contains one of two possible values: left or right value.

// Either instances can be created:
var left1 = Either<int, string>.Left(1);             // via factory method
var left2 = Left<int, string>(2);                    // via factory method in non-generic static Either class
Either<int, string> left3 = 3;                       // by implicit conversion

var right4 = Either<int, string>.Right("4");         // via factory method
var right5 = Right<int, string>("5");                // via factory method in non-generic static Either class
Either<int, string> right6 = "6";                    // by implicit conversion

// Supports left and right Map/FlatMap:
var mapLeft1 = left1.MapLeft(left => left + 10);             // == Left(11)
var mapRight1 = left1.MapRight(right => $"Right: {right}");  // == Left(1)
var mapRight2 = right4.MapRight(right => $"Right: {right}"); // == Right("Right: 4")

var flatMapLeft1 = left1.FlatMapLeft(left => Left<int, string>(left + 10));              // == Left(11)
var flatMapRight1 = left1.FlatMapLeft(_ => Right<int, string>($"Right: 1"));             // == Right("Right: 1")
var flatMapRight2 = right4.FlatMapRight(right => Right<int, string>($"Right: {right}")); // == Right("Right: 5")

// Matching by invoking corresponding function:
var matchResult = left1.Match(                      // == "Left: 1"
    left => $"Left: {left}",
    right => $"Right: {right}");

// Matching by invoking corresponding action: 
void PrintLeft<TLeft>(TLeft left) => Console.WriteLine($"Left: {left}");
void PrintRight<TRight>(TRight right) => Console.WriteLine($"Right: {right}");

left1.On(PrintLeft, PrintRight);        // Prints "Left: 1"
left2.OnLeft(PrintLeft);                // Prints "Left: 2"
right5.OnRight(PrintRight);             // Prints "Right: 5"
right5.OnLeft(PrintLeft);               // PrintLeft isn't invoked.

// Extracting left and right values as Option<TLeft> or Option<TRight>:
var some1 = left1.Left();            // == Some(1)
var none1 = left1.Right();           // == None

var none2 = right5.Left();           // == None
var some2 = right5.Right();          // == Some("5")

Pipe extensions

Pipe extensions allow chaining function calls.

// Functions used in examples
int Square(int x) => x * x;
int Add5(int x) => x + 5;

async Task<int> DoubleAsync(int x) => await Task.FromResult(x * 2);
async Task<int> Subtract2Async(int x) => await Task.FromResult(x - 2);

// Chaining functions:
var result1 = 10                          // == 55
    .Pipe(Square)
    .Pipe(x => x / 2)
    .Pipe(Add5);

// Chaining async functions:
var result2 = await 3                     // == 64
    .PipeAsync(DoubleAsync)
    .PipeAsync(Subtract2Async)
    .PipeAsync(async x => await Task.FromResult(x * x * x));
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 netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  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.0

    • 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
1.0.5 100 10/26/2024
1.0.4 79 10/14/2024
1.0.3 108 9/12/2024
1.0.2 164 6/19/2024
1.0.1 153 5/30/2024
1.0.0 157 5/27/2024