Option Library is a C# representation of the maybe monad which provides a good way to deal with the exceptional cases by reducing the number of exceptions and null checkings as well as the nested blocks in the code which leads to a more compact and more readable code base.
Option
.From(db.Players.FirstOrDefault(p => p.Name == keyword))
.Try(p => Console.WriteLine(p.Name))
.Select(p => db.Teams.Find(t => t.Id == p.TeamId))
.Try(t => Console.WriteLine(t.Name))
Instead of using nested if statements which leads to less readable code and hard to follow arrow anti pattern
var player = db.Players.FirstOrDefault(p => p.Name == keyword);
if (player != null)
{
Console.WriteLine(player.Name);
var team = db.Teams.Find(t => t.Id == player.Id);
if (team != null)
{
Console.WriteLine(team.Name);
}
}
For simplicity you can think of Nullable<T>
being closest thing to the maybe monad in C#. Although it doesn't represent all the characteristics of the maybe monad, it's sufficient enough to give the main idea.
At first glance, extending Nullable<T>
might be a good option,
but the struct
constraint for T
in it makes it impossible to use with the reference types.
Our hardworking teammates are trying to publish the Option Library to nuget , but until then you can download/clone/fork this repository and build it locally.
Option library targets .NET Standard 2.0 , so you can use it with .NET Core 2.0+, .NET Framework 4.6.1+ and many other platforms. Please, see the platform support.
Some
var number = Option.Some(23);
var str = Option.Some("apple");
Debug.Assert(number.HasValue);
Debug.Assert(str.HasValue);
None
-
var nan = Option.None<int>();
var empty = Option.None<string>();
Debug.Assert(!nan.HasValue);
Debug.Assert(!empty.HasValue);
From
- if you don't have the value in compile time
var copy = Option.From("apple".Or(null));
Debug.Assert(copy.Value == "apple"); // Option.Some("apple")
// or
Debug.Assert(!copy.HasValue); // Option.None<string>()
Try
- try possible exception throwing statements safely
int zero = 0;
var success = Option.Try(() => 4 / 2);
var fail = Option.Try(() => 1 / zero);
Debug.Assert(success.HasValue); // Option.Some(2)
Debug.Assert(!fail.HasValue); // Option.None<int>()
var defaultValue = number.Value; //not safe. might throw InvalidOperationException
var unsafeValue = nan.Value; //not safe. might throw InvalidOperationException
var safeValue = nan.ValueOrNull; // safe. returns Value or default(T)
var alternativeValue = nan.ValueOr(-1); //safe. returns Value or -1 as an alternative value
Convert to Nullable<T>
var nullableValue = number.ToNullable();
var nanValue = nan.ToNullable();
int power(int n) => n * n;
var anotherNumber = number.Select(power);
var anotherNan = nan.Select(power);
Debug.Assert(anotherNumber.Value == 23 * 23); // Option.Some(23 * 23)
Debug.Assert(!anotherNan.HasValue); // Option.None<int>()
Option<int> length(string s) => Option.From(s.Length);
var strLength = str.SelectMany(length);
var emptyLength = empty.SelectMany(length);
Debug.Assert(strLength.Value == 5); // Option.Some(5)
Debug.Assert(!emptyLength.HasValue); // Option.None<int>()
Please see CONTRIBUTING.md.
This is released under a modified version of the BSD licence. Please see LICENCE.md.