RandomSkunk.Results
1.0.0-alpha13
See the version list below for details.
dotnet add package RandomSkunk.Results --version 1.0.0-alpha13
NuGet\Install-Package RandomSkunk.Results -Version 1.0.0-alpha13
<PackageReference Include="RandomSkunk.Results" Version="1.0.0-alpha13" />
paket add RandomSkunk.Results --version 1.0.0-alpha13
#r "nuget: RandomSkunk.Results, 1.0.0-alpha13"
// Install RandomSkunk.Results as a Cake Addin
#addin nuget:?package=RandomSkunk.Results&version=1.0.0-alpha13&prerelease
// Install RandomSkunk.Results as a Cake Tool
#tool nuget:?package=RandomSkunk.Results&version=1.0.0-alpha13&prerelease
RandomSkunk.Results
This library contains three result types: Result<T>
, which represents a result that has a required value; Maybe<T>
, which represents a result that has an optional value; and Result
, which represents a result that does not have a value.
Usage
Creation
To create a result, use one of the static factory methods.
// Results that have a required value:
Result<int> result1 = Result<int>.Create.Success(123);
Result<int> result1a = 123.ToResult(); // using RandomSkunk.Result.FactoryExtensions;
Result<int> result2 = Result<int>.Create.Fail();
// Results that have an optional value:
Maybe<int> result3 = Maybe<int>.Create.Some(123);
Maybe<int> result3a = 123.ToMaybe(); // using RandomSkunk.Result.FactoryExtensions;
Maybe<int> result4 = Maybe<int>.Create.None();
Maybe<int> result5 = Maybe<int>.Create.Fail();
Maybe<string> result6 = Maybe<string>.Create.FromValue("abc"); // Some("abc")
Maybe<string> result7 = Maybe<string>.Create.FromValue(null); // None
// Results that do not have a value:
Result result8 = Result.Create.Success();
Result result9 = Result.Create.Fail();
Handling Results
There are two options for handling a result: by calling the Match
or MatchAsync
extension methods, which is safe but indirect; and GetValue()
or GetError()
extension methods, which is direct but unsafe.
Match methods
There are four variations of match methods for each of the result types, depending on what kind of work needs to be done. All the methods take parameters that are functions (delegates). Two of the methods are synchronous and the other two are asynchronous (Match
vs MatchAsync
). Divided the other way, two of the methods have function parameters that return a value, and the other two have function parameters that do not have a return value (i.e. they return void
or Task
).
Note that calling the match methods will never throw an exception unless the provided function parameters themselves throw when called.
// Synchronous functions with no return value (return void):
Maybe<int> result = ...
result.Match(
some: value => Console.WriteLine($"Some: {value}"),
none: () => Console.WriteLine("None"),
fail: error => Console.WriteLine($"Fail: {error}"));
// Asynchronous functions with no return value (return Task):
Result<int> result = ...
await result.MatchAsync(
success: async value => await Console.Out.WriteLineAsync($"Success: {value}"),
fail: async error => await Console.Out.WriteLineAsync($"Fail: {error}"));
// Synchronous functions with a return value:
Result result = ...
string message = result.Match(
success: () => "Success",
fail: error => $"Fail: {error}");
// Asynchronous functions with a return value:
Maybe<Guid> userIdResult = result3;
string message = await userIdResult.MatchAsync(
some: async userId =>
{
string userName = await GetUserName(userId);
return $"Hello, {userName}!";
},
none: () => Task.FromResult("Unknown user"),
fail: error => Task.FromResult("Error"));
Unsafe Access
To access the value and error of results directly, add using RandomSkunk.Results.Unsafe;
to the using directives, then call the GetValue()
and GetError()
extension methods on the result. Note that calling these extension methods will throw an InvalidStateException
if not in the proper state. For ResultType<T>
, IsSuccess
must be true in order to successfully call GetValue()
, and for Maybe<T>
, IsSome
must be true in order to successfully call GetValue()
. For all result types, IsFail
must be true in order to successfully call GetError()
.
// Required return value:
Result<int> result1 = ...
if (result1.IsFail)
Console.WriteLine($"Error: {result1.GetError()}");
else
Console.WriteLine($"Success: {result1.GetValue()}");
// Optional return value:
Maybe<int> result2 = ...
if (result2.IsSome)
Console.WriteLine($"Some: {result2.GetValue()}");
else if (result2.IsNone)
Console.WriteLine("None");
else
Console.WriteLine($"Error: {result2.GetError()}");
// No return value:
Result result3 = ...
if (result3.IsSuccess)
Console.WriteLine("Success");
else
Console.WriteLine($"Error: {result3.GetError()}");
Each result type also has an enum Type
property that returns the kind of result: Success
, Fail
, Some
, or None
, depending on the result type.
Maybe<int> result = ...
switch (result.Type)
{
case MaybeType.Some:
Console.WriteLine($"Some: {result.GetValue()}");
break;
case MaybeType.None:
Console.WriteLine("None");
break;
case MaybeType.Fail:
Console.WriteLine($"Fail: {result.GetError()}");
break;
}
Custom Errors and Factory Methods
Custom errors can be created by inheriting from the Error
class, then passed to a Fail
factory method.
public class NotFoundError : Error
{
public NotFoundError(int id, string resourceType = "record")
: base(
message: $"A {resourceType} with the ID {id} could not be found.",
errorCode: 404)
{
}
}
// Create a fail result with our custom error.
Result<T> result = Result<T>.Create.Fail(new NotFoundError(123));
// errorType: "NotFoundError"
string errorType = result.Error.Type;
// errorMessage: "A record with the ID 123 could not be found."
string errorMessage = result.Error.Message;
// errorCode: 404
string errorCode = result.Error.ErrorCode;
To make it easier to create specific fail results, extension methods targeting IResultFactory
, IResultFactory<T>
, or IMaybeFactory<T>
can be created.
public static class ResultFactoryExtensions
{
public static Result<T> NotFound<T>(
this IResultFactory<T> resultFactory,
int id,
string resourceType = "record")
{
return resultFactory.Fail(new NotFoundError(id, resourceType));
}
}
// Create a fail result with our factory extension method.
Result<T> result = Result<int>.Create.NotFound(123);
Result Extension Methods
There are numerous extension methods for the result types, though most are only applicable to Result<T>
and Maybe<T>
(not Result
).
Equals
Applicable to Result<T>
and Maybe<T>
only.
Compares a result to a value. true if the source result is Success
or Some
and its value equals the specified value.
Result<int>.Create.Success(123).Equals(123); // true
Result<int>.Create.Success(123).Equals(456); // false
Result<int>.Create.Fail().Equals(123); // false
Maybe<int>.Create.Some(123).Equals(123); // true
Maybe<int>.Create.Some(123).Equals(456); // false
Maybe<int>.Create.None().Equals(123); // false
Maybe<int>.Create.Fail().Equals(123); // false
GetValueOr
Applicable to Result<T>
and Maybe<T>
only.
Gets the value of a result if it is Success
or Some
, otherwise returns the specified fallback value.
Result<int>.Create.Success(123).GetValueOr(456); // 123
Result<int>.Create.Fail().GetValueOr(456); // 456
Maybe<int>.Create.Some(123).GetValueOr(456); // 123
Maybe<int>.Create.None().GetValueOr(456); // 456
Maybe<int>.Create.Fail().GetValueOr(456); // 456
Or
Applicable to Result<T>
and Maybe<T>
only.
the source result if it is Success
or Some
, otherwise returns a new Success
or Some
result with the specified fallback value.
Result<int>.Create.Success(123).Or(456); // Success(123)
Result<int>.Create.Fail().Or(456); // Success(456)
Maybe<int>.Create.Some(123).Or(456); // Some(123)
Maybe<int>.Create.None().Or(456); // Some(456)
Maybe<int>.Create.Fail().Or(456); // Some(456)
Else
Applicable to all three result types.
the source result if it is Success
or Some
, else returns the specified fallback result.
Result<int>.Create.Success(123).Else(Result<int>.Create.Success(456)); // Success(123)
Result<int>.Create.Success(123).Else(Result<int>.Create.Fail()); // Success(123)
Result<int>.Create.Fail("A").Else(Result<int>.Create.Success(456)); // Success(456)
Result<int>.Create.Fail("A").Else(Result<int>.Create.Fail("B")); // Fail("B")
Maybe<int>.Create.Some(123).Else(Maybe<int>.Create.Some(456)); // Some(123)
Maybe<int>.Create.Some(123).Else(Maybe<int>.Create.None()); // Some(123)
Maybe<int>.Create.Some(123).Else(Maybe<int>.Create.Fail("B")); // Some(123)
Maybe<int>.Create.None().Else(Maybe<int>.Create.Some(456)); // Some(456)
Maybe<int>.Create.None().Else(Maybe<int>.Create.None()); // None
Maybe<int>.Create.None().Else(Maybe<int>.Create.Fail("B")); // Fail("B")
Maybe<int>.Create.Fail("A").Else(Maybe<int>.Create.Some(456)); // Some(456)
Maybe<int>.Create.Fail("A").Else(Maybe<int>.Create.None()); // None
Maybe<int>.Create.Fail("A").Else(Maybe<int>.Create.Fail("B")); // Fail("B")
Map / MapAsync
Applicable to Result<T>
and Maybe<T>
only.
Transforms the source result into a new result. If the source result is Success
or Some
, return a new Success
or Some
result with its value obtained by evaluating the specified map
or mapAsync
function. If the source result is Fail
, return a new Fail
result with the same error as the source. If the source result is None
, return None
.
Result<int>.Create.Success(123).Map(value => value.ToString()); // Success("123")
Result<int>.Create.Fail("A").Map(value => value.ToString()); // Fail("A")
Maybe<int>.Create.Some(123).Map(value => value.ToString()); // Some("123")
Maybe<int>.Create.None().Map(value => value.ToString()); // None
Maybe<int>.Create.Fail("A").Map(value => value.ToString()); // Fail("A")
Each of the Map
and MapAsync
overloads has an optional Func<Error, Error> getError
parameter, which allows the caller to replace the error of the returned Fail
result. This function is evaluated only if the source result is a Fail
result.
FlatMap / FlatMapAsync
Applicable to Result<T>
and Maybe<T>
only.
Transforms the source result into a new result. If the source result is Success
or Some
, returns the result obtained by evaluating the specified flatMap
or flatMapAsync
function. If the source result is Fail
, returns a new Fail
result with the same error as the source. If the source result is None
, returns None
.
Result<int>.Create.Success(123).FlatMap(GetSuccessResult); // Success("123")
Result<int>.Create.Success(123).FlatMap(GetFailResult); // Fail
Result<int>.Create.Fail("A").FlatMap(GetSuccessResult); // Fail
Result<int>.Create.Fail("A").FlatMap(GetFailResult); // Fail
Maybe<bool>.Create.Some(true).FlatMap(GetSomeResult); // Some("true")
Maybe<bool>.Create.Some(true).FlatMap(GetNoneResult); // None
Maybe<bool>.Create.Some(true).FlatMap(GetFailResult); // Fail("B")
Maybe<bool>.Create.None().FlatMap(GetSomeResult); // None
Maybe<bool>.Create.None().FlatMap(GetNoneResult); // None
Maybe<bool>.Create.None().FlatMap(GetFailResult); // None
Maybe<bool>.Create.Fail("A").FlatMap(GetSomeResult); // Fail("A")
Maybe<bool>.Create.Fail("A").FlatMap(GetNoneResult); // Fail("A")
Maybe<bool>.Create.Fail("A").FlatMap(GetFailResult); // Fail("A")
Result<string> GetSuccessResult(int value) => Result<string>.Create.Success(value.ToString());
Result<string> GetFailResult(int value) => Result<string>.Create.Fail("B");
Maybe<string> GetSomeResult(bool value) => Maybe<string>.Create.Some(value.ToString());
Maybe<string> GetNoneResult(bool value) => Maybe<string>.Create.None();
Maybe<string> GetFailResult(bool value) => Maybe<string>.Create.Fail("B");
Each of the FlatMap
and FlatMapAsync
overloads has an optional Func<Error, Error> getError
parameter, which allows the caller to replace the error of the returned Fail
result. This function is evaluated only if the source result is a Fail
result.
CrossMap / CrossMapAsync
Applicable to Result<T>
and Maybe<T>
only.
Transforms the source result into a different type of result.
// Result<T> to Result
Result<int>.Create.Success(123).CrossMap(GetSuccessResult); // Success
Result<int>.Create.Success(123).CrossMap(GetFailResult); // Fail
Result<int>.Create.Fail("A").CrossMap(GetSuccessResult); // Fail("A")
Result<int>.Create.Fail("A").CrossMap(GetFailResult); // Fail("A")
// Result<T> to Maybe<T>
Result<int>.Create.Success(123).CrossMap(GetSomeMaybeOfString); // Some("123")
Result<int>.Create.Success(123).CrossMap(GetNoneMaybeOfString); // None
Result<int>.Create.Success(123).CrossMap(GetFailMaybeOfString); // Fail("B")
Result<int>.Create.Fail("A").CrossMap(GetSomeMaybeOfString); // Fail("A")
Result<int>.Create.Fail("A").CrossMap(GetNoneMaybeOfString); // Fail("A")
Result<int>.Create.Fail("A").CrossMap(GetFailMaybeOfString); // Fail("A")
// Maybe<T> to Result
Maybe<int>.Create.Some(123).CrossMap(GetSuccessResult); // Success
Maybe<int>.Create.Some(123).CrossMap(GetFailResult); // Fail("B")
Maybe<int>.Create.None().CrossMap(GetSuccessResult); // Fail(none)
Maybe<int>.Create.None().CrossMap(GetFailResult); // Fail(none)
Maybe<int>.Create.Fail("A").CrossMap(GetSuccessResult); // Fail("A")
Maybe<int>.Create.Fail("A").CrossMap(GetFailResult); // Fail("A")
// Maybe<T> to Result<T>
Maybe<int>.Create.Some(123).CrossMap(GetSuccessResultOfString); // Success("123")
Maybe<int>.Create.Some(123).CrossMap(GetFailResultOfString); // Fail("B")
Maybe<int>.Create.None().CrossMap(GetSuccessResultOfString); // Fail(none)
Maybe<int>.Create.None().CrossMap(GetFailResultOfString); // Fail(none)
Maybe<int>.Create.Fail("A").CrossMap(GetSuccessResultOfString); // Fail("A")
Maybe<int>.Create.Fail("A").CrossMap(GetFailResultOfString); // Fail("A")
Result GetSuccessResult(int value) => Result.Create.Success();
Result GetFailResult(int value) => Result.Create.Fail("B");
Result<string> GetSuccessResultOfString(int value) => Result<string>.Create.Success(value.ToString());
Result<string> GetFailResultOfString(int value) => Result<string>.Create.Fail("B");
Maybe<string> GetSomeMaybeOfString(int value) => Maybe<string>.Create.Some(value.ToString());
Maybe<string> GetNoneMaybeOfString(int value) => Maybe<string>.Create.None();
Maybe<string> GetFailMaybeOfString(int value) => Maybe<string>.Create.Fail("B");
Each of the CrossMap
and CrossMapAsync
overloads has an optional Func<Error, Error> getError
parameter, which allows the caller to replace the error of the returned Fail
result when the source result is a Fail
result.
The cross-map methods for Maybe<T>
also have an optional Func<Error> getNoneError
parameter, which allows the caller to specify the error of the returned Fail
result when the source result is a None
result.
Flatten
Applicable to Result<T>
and Maybe<T>
only.
Flattens a Result<Result<T>>
into a Result<T>
or a Maybe<Maybe<T>>
into a Maybe<T>
.
Result<Result<int>> nestedResult;
Result<int> flattenedResult = nestedResult.Flatten();
Maybe<Maybe<int>> nestedMaybe;
Maybe<int> flattenedMaybe = nestedMaybe.Flatten();
Filter / FilterAsync
Applicable to Maybe<T>
only.
Filters a Some
result to None
unless the specified filter function evaluates to true. None
and Fail
results are not affected.
Maybe<int>.Create.Some(123).Filter(value => value < 150); // Some(123)
Maybe<int>.Create.Some(456).Filter(value => value < 150); // None
Maybe<int>.Create.None().Filter(value => value < 150); // None
Maybe<int>.Create.Fail("A").Filter(value => value < 150); // Fail("A")
WithError
Applicable to all three result types.
Returns a new result with a different error if the source is a Fail
result. Success
, Some
, and None
result are not affected.
Result failResult = Result.Create.Fail("Inner error");
Result successResult = Result.Create.Success();
// Fail("Outer error"("Inner error"))
failResult.WithError(error => new Error("Outer error", innerError: error));
// Success
successResult.WithError(error => new Error("Outer error", innerError: error));
LINQ Extension Methods
In the RandomSkunk.Results.Linq
namespace, there are aliases for the Map
, FlatMap
, and Filter
extension methods named Select
, SelectMany
, and Where
. These methods allow you to use LINQ to transform results.
using RandomSkunk.Results.Linq;
// Given methods with the following signatures:
Maybe<Person> GetPerson(Guid id)
Maybe<Department> GetDepartment(Guid id)
Guid personId;
// Chain the results together:
Maybe<Department> result =
from person in GetPerson(personId)
where person.IsActive
from department in GetDepartment(person.DepartmentId)
select department;
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. 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 is compatible. |
.NET Framework | net461 is compatible. 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. |
-
.NETFramework 4.6.1
- System.Text.Json (>= 6.0.4)
-
.NETStandard 2.0
- System.Text.Json (>= 6.0.4)
-
.NETStandard 2.1
- System.Text.Json (>= 6.0.4)
-
net6.0
- System.Text.Json (>= 6.0.4)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on RandomSkunk.Results:
Package | Downloads |
---|---|
RandomSkunk.Results.AspNetCore
Using RandomSkunk.Results from ASP.NET Core applications. |
|
RandomSkunk.Results.Http
Using RandomSkunk.Results with System.Net.Http and System.Net.Http.Json. |
|
RandomSkunk.Results.Dapper
Using RandomSkunk.Results with Dapper. |
GitHub repositories
This package is not used by any popular GitHub repositories.