dotMaybe 1.0.0
dotnet add package dotMaybe --version 1.0.0
NuGet\Install-Package dotMaybe -Version 1.0.0
<PackageReference Include="dotMaybe" Version="1.0.0" />
paket add dotMaybe --version 1.0.0
#r "nuget: dotMaybe, 1.0.0"
// Install dotMaybe as a Cake Addin #addin nuget:?package=dotMaybe&version=1.0.0 // Install dotMaybe as a Cake Tool #tool nuget:?package=dotMaybe&version=1.0.0
dotMaybe - The Maybe Monad for .NET
dotMaybe is a lightweight, intuitive implementation of the Maybe monad for .NET. It simplifies working with optional values, eliminating null reference exceptions and promoting a more functional, declarative programming style.
Give it a star ⭐!
If you find this project valuable, please consider giving it a star! Your support helps others discover this work and encourages further development.
Maybe<T>
API Documentation
Overview
The Maybe<T>
monad represents a value that may or may not exist, encapsulating the concept of optional data. It provides methods to safely handle, transform, and operate on potentially absent values, offering a robust alternative to null references in both synchronous and asynchronous contexts.
Properties
IsSome
Gets a value indicating whether the maybe contains a value.
public bool IsSome { get; }
Example:
Maybe<int> maybe = Some.With(42);
if (maybe.IsSome)
{
Console.WriteLine("Contains value!");
}
IsNone
Gets a value indicating whether the maybe is empty.
public bool IsNone { get; }
Example:
Maybe<int> maybe = None.OfType<int>();
if (maybe.IsNone)
{
Console.WriteLine("Is empty!");
}
Methods
Match
Executes one of two provided functions based on whether the Maybe<T>
contains a value or not.
public TResult Match<TResult>(Func<TResult> none, Func<T, TResult> some)
Example:
Maybe<int> maybe = Some.With(42);
string message = maybe.Match(
() => "Is empty",
value => "Value: " + value); // "Value: 42"
Maybe<int> maybe = None.OfType<int>();
string message = maybe.Match(
() => "Is empty",
value => "Value: " + value); // "Is empty"
MatchAsync
Asynchronously executes one of two provided functions based on whether the Maybe<T>
contains a value or not.
With both asynchronous functions
public async Task<TResult> MatchAsync<TResult>(Func<Task<TResult>> none, Func<T, Task<TResult>> some)
Example:
Maybe<int> maybe = Some.With(42);
string message = await maybe.MatchAsync(
async () => await Task.FromResult("Is empty"),
async value => await Task.FromResult("Value: " + value)); // "Value: 42"
Maybe<int> maybe = None.OfType<int>();
string message = await maybe.MatchAsync(
async () => await Task.FromResult("Is empty"),
async value => await Task.FromResult("Value: " + value)); // "Is empty"
With only function for some being asynchronous
public async Task<TResult> MatchAsync<TResult>(Func<TResult> none, Func<T, Task<TResult>> some)
Example:
Maybe<int> maybe = Some.With(42);
string message = await maybe.MatchAsync(
() => "Is empty",
async value => await Task.FromResult("Value: " + value)); // "Value: 42"
Maybe<int> maybe = None.OfType<int>();
string message = await maybe.MatchAsync(
() => "Is empty",
async value => await Task.FromResult("Value: " + value)); // "Is empty"
Map
Transforms the value inside a Maybe<T> using the provided mapping function, if a value exists.
public Maybe<TResult> Map<TResult>(Func<T, TResult> map)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<string> mappedMaybe = maybe.Map(value => value.ToString()); // Some with "42"
Maybe<int> maybe = None.OfType<int>();
Maybe<string> mappedMaybe = maybe.Map(value => value.ToString()); // None
MapAsync
Asynchronously transforms the value inside a Maybe<T> using the provided mapping function, if a value exists.
public Task<Maybe<TResult>> MapAsync<TResult>(Func<T, Task<TResult>> map)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<string> mappedMaybe = await maybe.MapAsync(
async value => await Task.FromResult(value.ToString())); // Some with "42"
Maybe<int> maybe = None.OfType<int>();
Maybe<string> mappedMaybe = await maybe.MapAsync(
async value => await Task.FromResult(value.ToString())); // None
Bind
Chains Maybe operations by applying a function that returns a new Maybe, allowing for composition of operations that might fail.
public Maybe<TResult> Bind<TResult>(Func<T, Maybe<TResult>> bind)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<string> boundMaybe = maybe.Bind(
value => Some.With(value.ToString())); // Some with "42"
Maybe<int> maybe = Some.With(42);
Maybe<string> boundMaybe = maybe.Bind(
value => None.OfType<string>()); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<string> boundMaybe = maybe.Bind(
value => Some.With(value.ToString())); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<string> boundMaybe = maybe.Bind(
value => None.OfType<string>()); // None
BindAsync
Chains Maybe operations by asynchronously applying a function that returns a new Maybe, allowing for composition of operations that might fail.
public Task<Maybe<TResult>> BindAsync<TResult>(Func<T, Task<Maybe<TResult>>> bind)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<string> boundMaybe = await maybe.BindAsync(
async value => await Task.FromResult(Some.With(value.ToString()))); // Some with "42"
Maybe<int> maybe = Some.With(42);
Maybe<string> boundMaybe = await maybe.BindAsync(
async value => await Task.FromResult(None.OfType<string>())); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<string> boundMaybe = await maybe.BindAsync(
async value => await Task.FromResult(Some.With(value.ToString()))); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<string> boundMaybe = await maybe.BindAsync(
async value => await Task.FromResult(None.OfType<string>())); // None
Fold
Reduces the Maybe<T>
to a single value by applying a folder function if a value exists, otherwise returns the initial state.
public TState Fold<TState>(TState state, Func<TState, T, TState> folder)
Example:
var maybe = None.OfType<int>();
var foldedResult = maybe.Fold(0, (acc, value) => acc + value); // 0
var maybe = Some.With(42);
var foldedResult = maybe.Fold(0, (acc, value) => acc + value); // 42
var maybe = Some.With(42);
var foldedResult = maybe.Fold(10, (acc, value) => acc + value); // 52
Filter
Applies a predicate to the value in Maybe<T>, returning None if the predicate fails or the value doesn't exist.
public public Maybe<T> Filter(Predicate<T> predicate)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<int> filteredMaybe = maybe.Filter(value => value < 50); // Some with 42
Maybe<int> maybe = Some.With(52);
Maybe<int> filteredMaybe = maybe.Filter(value => value < 50); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<int> filteredMaybe = maybe.Filter(value => value < 50); // None
FilterAsync
Applies an asynchronous predicate to the value in Maybe<T>, returning None if the predicate fails or the value doesn't exist.
public public Task<Maybe<T>> FilterAsync(Func<T, Task<bool>> predicate)
Example:
Maybe<int> maybe = Some.With(42);
Maybe<int> filteredMaybe = await maybe.FilterAsync(
async value => await Task.FromResult(value < 50)); // Some with 42
Maybe<int> maybe = Some.With(52);
Maybe<int> filteredMaybe = await maybe.FilterAsync(
async value => await Task.FromResult(value < 50)); // None
Maybe<int> maybe = None.OfType<int>();
Maybe<int> filteredMaybe = await maybe.FilterAsync(
async value => await Task.FromResult(value < 50)); // None
OrDefault
Returns the value if it exists, otherwise returns the specified default value.
public T OrDefault(T defaultValue)
Example:
Maybe<int> maybe = None.OfType<int>();
int value = maybe.OrDefault(100); // 100
Maybe<int> result = Some.With(42);
int value = result.OrDefault(100); // 42
OrDefault
Returns the value if it exists, otherwise invokes the provided factory function to create a default value.
public T OrDefault(Func<T> defaultFactory)
Example:
Maybe<int> maybe = None.OfType<int>();
int value = maybe.OrDefault(() => 100); // 100
Maybe<int> result = Some.With(42);
int value = result.OrDefault(() => 100); // 42
Static Methods
Implicit conversion
Create a Maybe instance containing the specified value. Or None if the value is null.
public static implicit operator Maybe<T>(T? value)
Example:
Maybe<int> maybe = 42; // Some with 42
int? x = null;
Maybe<int> maybe = x; // None
Flatten
Flattens a nested Maybe, reducing Maybe<Maybe<T>> to Maybe<T>.
public static Maybe<T> Flatten<T>(this Maybe<Maybe<T>> nested)
Example:
Maybe<Maybe<int>> nested = 42.ToMaybe().ToMaybe();
Maybe<int> flattened = nested.Flatten();
T extensions
ToMaybe
Convert a value to a Maybe instance.
Non-nullable value types and reference types
public static Maybe<T> ToMaybe<T>(this T value)
Example:
Maybe<string> maybe = "Hello, World!".ToMaybe(); // Some with "Hello, World!"
string? x = null;
Maybe<string> maybe = x.ToMaybe(); // None
Nullable value types
public static Maybe<T> ToMaybe<T>(this T? value) where T : struct
Example:
Maybe<int> maybe = 42.ToMaybe(); // Some with 42
int? x = null;
Maybe<int> maybe = x.ToMaybe(); // None
IEnumerable<T> extensions
FirstOrNone
Returns the first element of a sequence as a Maybe, or an empty Maybe if the sequence contains no elements.
Without predicate
public static Maybe<T> FirstOrNone<T>(this IEnumerable<T> source)
Example:
IEnumerable<int> collection = [42];
Maybe<int> maybe = collection.FirstOrNone(); // Some with 42
IEnumerable<int> collection = [];
Maybe<int> maybe = collection.FirstOrNone(); // None
With predicate
public static Maybe<T> FirstOrNone<T>(
this IEnumerable<T> source,
Func<T, bool> predicate)
Example:
IEnumerable<int> collection = [42];
Maybe<int> maybe = collection.FirstOrNone(v => v > 40); // Some with 42
IEnumerable<int> collection = [42];
Maybe<int> maybe = collection.FirstOrNone(v => v < 40); // None
IEnumerable<int> collection = [];
Maybe<int> maybe = collection.FirstOrNone(v => v > 0); // None
Static Methods on Static Class Maybe
Map2
Combines two Maybe instances using a mapping function.
public static Maybe<TResult> Map2<T1, T2, TResult>(
Maybe<T1> maybe1,
Maybe<T2> maybe2,
Func<T1, T2, TResult> map)
Example:
Maybe<string> result = Maybe.Map3(
Some.With(1),
Some.With("Hello there!"),
(v1, v2) => $"{v1}: {v3}"); // Some with "1: Hello there!"
Maybe<string> result = Maybe.Map2(
None.OfType<int>(),
Some.With("Hello there!"),
(v1, v2) => $"{v1}: {v3}"); // None
Maybe<string> result = Maybe.Map2(
Some.With(1),
None.OfType<string>(),
(v1, v2) => $"{v1}: {v3}"); // None
Map3
Combines three Maybe instances using a mapping function.
public static Maybe<TResult> Map3<T1, T2, T3, TResult>(
Maybe<T1> maybe1,
Maybe<T2> maybe2,
Maybe<T3> maybe3,
Func<T1, T2, T3, TResult> map)
Example:
Maybe<string> result = Maybe.Map3(
Some.With(1),
Some.With("abcd"),
Some.With("Hello there!"),
(v1, v2, v3) => $"{v1} ({v2}): {v3}"); // Some with "1 (abcd): Hello there!"
Maybe<string> result = Maybe.Map3(
None.OfType<int>(),
Some.With("abcd"),
Some.With("Hello there!"),
(v1, v2, v3) => $"{v1} ({v2}): {v3}"); // None
Maybe<string> result = Maybe.Map3(
Some.With(1),
None.OfType<string>(),
Some.With("Hello there!"),
(v1, v2, v3) => $"{v1} ({v2}): {v3}"); // None
Maybe<string> result = Maybe.Map3(
Some.With(1),
Some.With("abcd"),
None.OfType<string>(),
(v1, v2, v3) => $"{v1} ({v2}): {v3}"); // None
Query syntax
Select
Transforms the value of a Maybe wrapped in a Task into a new Maybe.
With synchronous selector
public Maybe<TResult> Select<TResult>(Func<T, TResult> selector)
Example:
Maybe<string> mappedMaybe =
from x in Some.With(42)
select x.ToString(); // Some with "42"
Maybe<string> mappedMaybe =
from x in None.OfType<int>
select x.ToString(); // None
With asynchronous selector
public Task<Maybe<TResult>> Select<TResult>(Func<T, Task<TResult>> selector)
Example:
Maybe<string> mappedMaybe = await (
from x in Some.With(42)
select Task.FromResult(x.ToString())); // Some with "42"
Maybe<string> mappedMaybe = await (
from x in None.OfType<int>
select Task.FromResult(x.ToString())); // None
SelectMany
Projects the value of a Maybe into a new Maybe, then flattens the result into a single Maybe.
With synchronous selectors
public Maybe<TResult> SelectMany<TIntermediate, TResult>(
Func<T, Maybe<TIntermediate>> intermediateSelector,
Func<T, TIntermediate, TResult> resultSelector)
Example:
Maybe<string> boundMaybe =
from v1 in Some.With(1)
from v2 in Some.With("Hello there!")
select $"{v1}: {v2}"; // Some with "1: Hello there!"
Maybe<string> boundMaybe =
from v1 in None.OfType<int>()
from v2 in Some.With("Hello there!")
select $"{v1}: {v2}"; // None
Maybe<string> boundMaybe =
from v1 in Some.With(1)
from v2 in None.OfType<string>()
select $"{v1}: {v2}"; // None
With asynchronous intermediate selector
public Task<Maybe<TResult>> SelectMany<TIntermediate, TResult>(
Func<T, Task<Maybe<TIntermediate>>> intermediateSelector,
Func<T, TIntermediate, TResult> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Some.With(1)
from v2 in Task.FromResult(Some.With("Hello there!"))
select $"{v1}: {v2}"); // Some with "1: Hello there!"
With asynchronous result selector
public Task<Maybe<TResult>> SelectMany<TIntermediate, TResult>(
Func<T, Maybe<TIntermediate>> intermediateSelector,
Func<T, TIntermediate, Task<TResult>> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Some.With(1)
from v2 in Some.With("Hello there!")
select Task.FromResult($"{v1}: {v2}")); // Some with "1: Hello there!"
With asynchronous intermediate and result selectors
public Task<Maybe<TResult>> SelectMany<TIntermediate, TResult>(
Func<T, Task<Maybe<TIntermediate>>> intermediateSelector,
Func<T, TIntermediate, Task<TResult>> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Some.With(1)
from v2 in Task.FromResult(Some.With("Hello there!"))
select Task.FromResult($"{v1}: {v2}")); // Some with "1: Hello there!"
Where
Applies a predicate to the value in Maybe<T>, returning None if the predicate fails or the value doesn't exist.
With synchronous predicate
public Maybe<T> Where(Predicate<T> predicate)
Example:
Maybe<int> filteredMaybe =
from x in Some.With(42)
where x > 40
select x; // Some with 42
Maybe<int> filteredMaybe =
from x in Some.With(42)
where x < 40
select x; // None
Maybe<int> filteredMaybe =
from x in None.OfType<int>()
where x < 40
select x; // None
With asynchronous predicate
public Task<Maybe<T>> Where(Func<T, Task<bool>> predicate)
Example:
Maybe<int> filteredMaybe = await (
from x in Some.With(42)
where Task.FromResult(x > 40)
select x); // Some with 42
Maybe<int> filteredMaybe = await (
from x in Some.With(42)
where Task.FromResult(x < 40)
select x); // None
Maybe<int> filteredMaybe = await (
from x in None.OfType<int>()
where Task.FromResult(x < 40)
select x); // None
Task<Maybe> Extensions
Select
Transforms the value of a Maybe wrapped in a Task into a new Maybe.
With synchronous selector
public static Task<Maybe<TResult>> Select<T, TResult>(
this Task<Maybe<T>> source,
Func<T, Task<TResult>> selector)
Example:
Maybe<string> mappedMaybe = await (
from x in Task.FromResult(Some.With(42))
select Task.FromResult(x.ToString())); // Some with "42"
Maybe<string> mappedMaybe = await (
from x in Task.FromResult(None.OfType<int>)
select Task.FromResult(x.ToString())); // None
With asynchronous selector
public static Task<Maybe<TResult>> Select<T, TResult>(
this Task<Maybe<T>> source,
Func<T, TResult> selector)
Example:
Maybe<string> mappedMaybe = await (
from x in Task.FromResult(Some.With(42))
select x.ToString()); // Some with "42"
Maybe<string> mappedMaybe = await (
from x in Task.FromResult(None.OfType<int>)
select x.ToString()); // None
SelectMany
Projects the value of a Maybe wrapped in a Task into a new Maybe, then flattens the result into a single Maybe.
With synchronous selectors
public static Task<Maybe<TResult>> SelectMany<T, TIntermediate, TResult>(
this Task<Maybe<T>> source,
Func<T, Maybe<TIntermediate>> intermediateSelector,
Func<T, TIntermediate, TResult> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Task.FromResult(Some.With(1))
from v2 in Some.With("Hello there!")
select $"{v1}: {v2}"); // Some with "1: Hello there!"
With asynchronous intermediate selector
public static Task<Maybe<TResult>> SelectMany<T, TIntermediate, TResult>(
this Task<Maybe<T>> source,
Func<T, Task<Maybe<TIntermediate>>> intermediateSelector,
Func<T, TIntermediate, TResult> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Task.FromResult(Some.With(1))
from v2 in Task.FromResult(Some.With("Hello there!"))
select $"{v1}: {v2}"); // Some with "1: Hello there!"
With asynchronous result selector
public static Task<Maybe<TResult>> SelectMany<T, TIntermediate, TResult>(
this Task<Maybe<T>> source,
Func<T, Maybe<TIntermediate>> intermediateSelector,
Func<T, TIntermediate, Task<TResult>> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Task.FromResult(Some.With(1))
from v2 in Some.With("Hello there!")
select Task.FromResult($"{v1}: {v2}")); // Some with "1: Hello there!"
With asynchronous intermediate and result selectors
public static Task<Maybe<TResult>> SelectMany<T, TIntermediate, TResult>(
this Task<Maybe<T>> source,
Func<T, Task<Maybe<TIntermediate>>> intermediateSelector,
Func<T, TIntermediate, Task<TResult>> resultSelector)
Example:
Maybe<string> boundMaybe = await (
from v1 in Task.FromResult(Some.With(1))
from v2 in Task.FromResult(Some.With("Hello there!"))
select Task.FromResult($"{v1}: {v2}")); // Some with "1: Hello there!"
Where
Applies a predicate to the value in Maybe<T> wrapped in a Task, returning None if the predicate fails or the value doesn't exist.
With synchronous predicate
public Task<Maybe<T>> Where<T>(this Task<Maybe<T>> source, Predicate<T> predicate)
Example:
Maybe<int> filteredMaybe =
from x in Task.FromResult(Some.With(42))
where x > 40
select x; // Some with 42
Maybe<int> filteredMaybe =
from x in Task.FromResult(Some.With(42))
where x < 40
select x; // None
Maybe<int> filteredMaybe =
from x in Task.FromResult(None.OfType<int>())
where x < 40
select x; // None
With asynchronous predicate
public Task<Maybe<T>> Where<T>(this Task<Maybe<T>> source, Func<T, Task<bool>> predicate)
Example:
Maybe<int> filteredMaybe = await (
from x in Task.FromResult(Some.With(42))
where Task.FromResult(x > 40)
select x); // Some with 42
Maybe<int> filteredMaybe = await (
from x in Task.FromResult(Some.With(42))
where Task.FromResult(x < 40)
select x); // None
Maybe<int> filteredMaybe = await (
from x in Task.FromResult(None.OfType<int>())
where Task.FromResult(x < 40)
select x); // None
Contribution
If you would like to contribute to this project, check out CONTRIBUTING file.
License
This project is licensed under the terms of the MIT license.
Product | Versions 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. |
-
.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.
- Add Select method with async selector for Maybe
- Add Select method for Task of Maybe
- Add Select method with async selector for Task of Maybe
- Add SelectMany method with async result selector for Maybe
- Add SelectMany method with async intermediate selector for Maybe
- Add SelectMany method with async intermediate and result selector for Maybe
- Add SelectMany method for Task of Maybe
- Add SelectMany method with async result selector for Task of Maybe
- Add SelectMany method with async intermediate selector for Task of Maybe
- Add SelectMany method with async intermediate and result selector for Task of Maybe
- Add FilterAsync method for Maybe
- Add Where method with async predicate for Maybe
- Add Where method for Task of Maybe
- Add Where method with async predicate for Task of Maybe