An open API service indexing awesome lists of open source software.

https://github.com/lucasteles/resulteles

A practical Result data structure implementation for C#
https://github.com/lucasteles/resulteles

csharp dotnet either monad result

Last synced: 8 months ago
JSON representation

A practical Result data structure implementation for C#

Awesome Lists containing this project

README

          

[![CI](https://github.com/lucasteles/Resulteles/actions/workflows/ci.yml/badge.svg)](https://github.com/lucasteles/Resulteles/actions/workflows/ci.yml)
[![Nuget](https://img.shields.io/nuget/v/Resulteles.svg?style=flat)](https://www.nuget.org/packages/Resulteles)
![](https://raw.githubusercontent.com/lucasteles/Resulteles/badges/badge_linecoverage.svg)
![](https://raw.githubusercontent.com/lucasteles/Resulteles/badges/badge_branchcoverage.svg)
![](https://raw.githubusercontent.com/lucasteles/Resulteles/badges/test_report_badge.svg)
![](https://raw.githubusercontent.com/lucasteles/Resulteles/badges/lines_badge.svg)

![](https://raw.githubusercontent.com/lucasteles/Resulteles/badges/dotnet_version_badge.svg)
![](https://img.shields.io/badge/Lang-C%23-green)
![https://editorconfig.org/](https://img.shields.io/badge/style-EditorConfig-black)

# Resulteles

**Resulteles is a practical and lightweight library that define and facilitate the use of a `success|failure` data
structure called `Result` (aka `Either`).**

## Getting started

[NuGet package](https://www.nuget.org/packages/Resulteles) available:

```ps
$ dotnet add package Resulteles
```

### Creating Results

You can create any result using the static methods on `Result`

```csharp
// explicitly
var resultSuccess = Result.Ok(42);
var resultError = Result.Error("Something wrong!");

var processResult = Result.Try(() => { return 1; });
var processAsyncResult = Result.TryAsync(async () => { return await Task.FromResult(2); });
```

In most of the scenarios you will be using result as a return of a method or function, because of that it
implements implicit convert operators from the generic types, reducing the generic type definition noise

```csharp
public Result GetEvenNumber(int number)
{
if (number % 2 is 0)
return number;
else
return $"{number} is not even!";
}
```

### Reading Result Value

The `Result` type acts like a blackbox, by default there is no way to just get the typed value mostly because it can be
two things, an `Ok` or an `Error`.

```csharp
Result result = GetEvenNumber(2);

// return -1 on error
int value1 = result.Match(
n => n,
error => -1
);

result.Switch(
n => Console.WriteLine($"the number {n} is even"),
error => Console.WriteLine(error)
);

// return 0 on error
var value2 = result.DefaultValue(0);

// lazily define the bad case value
var value3 = result.DefaultWith(e => e.Length);

// get the value or throw an exeption
var value4 = result.GetValueOrThrow();

// try/out pattern
if (result.TryGet(out var ok, out var error))
Console.WriteLine($"the number {ok} is even");
else
Console.WriteLine(error);
```

### Operators

Most types you not even need to extract the value, you can just apply functions or combine with other `Result` types:

```csharp
var result1 = Result.Ok(1);

// Map the result value only when it is Ok
var result2 = result1.Select(n => n + 1);

// map for ok or error case
Result result3 = result1.Select(n => n.ToString(), err => err.Length);

// SelectMany / Bind / FlatMamap
// Nested results transformation
Result resultCombined = result1.SelectMany(n => GetEvenNumber(n));

// Zip Results together into a tuple
// only resolves as Ok when both are Ok
Result<(int, int), string> resultZip = result1.Zip(result2);

// Zip Results together
// only resolves as Ok when both are Ok
Result resultSum = result1.Zip(result2, (a, b) => a + b);

// Enumerate result, empty when error, singleton when error (see ToArray() also)
IEnumerable resultEnumerable = result1.AsEnumerable();

// LINQ QUERY
// you can use linq query to easily express map and bind

// simple map
var result2Linq = from r in result1 select r + 1;

// combining multiple results (fail fast at first non ok value)
var resultEvenSum =
from a in GetEvenNumber(2)
from b in GetEvenNumber(4)
from c in GetEvenNumber(6)
select a + b + c;
}
```

### Async/Await

This library implements some helpers to seamless deal with async Result code

```csharp
var result = GetEvenNumber(2);

// async match overload
int value1 = await result.Match(
async n =>
{
await Task.Delay(n);
return n;
},
_ => -1
);

await result.SwitchAsync(
async n => await Console.Out.WriteLineAsync($"the number {n} is even"),
async error => await Console.Error.WriteLineAsync(error)
);

// async projection counterparts
Result resultWithDelay = await result.SelectAsync(async n =>
{
await Task.Delay(n);
return n;
});

// Transform Result, string> in a awaitable Task>
Result resultWithDelay2 = await result.Select(async n =>
{
await Task.Delay(n);
return n;
}).ToTask();
```

# Helper extensions

```csharp
// result collection
var results = new[] { GetEvenNumber(2), GetEvenNumber(3), GetEvenNumber(4) };

// filter ok values
IEnumerable okResultsOnly = results.GetOkValues(); // [2,4]

// map/reduce based on a result function
var numbers = new[] { 1, 2, 3, 4 };
var calculatedResults = numbers.ChooseResult(x => GetEvenNumber(x)); // [2,4]

// group a collection of result into a Result of collection
// collapse to error if any error is in the collection
Result, string> singleResult = results.GroupResults();

// Transform Result, string> in a awaitable Task>
Result resultWithDelay2 = await results[0].Select(async n =>
{
await Task.Delay(n);
return n;
}).ToTask();
```

# FluentAssertions Extensions

[NuGet package](https://www.nuget.org/packages/Resulteles.FluentAssertions) available:

```ps
$ dotnet add package Resulteles.FluentAssertions
```

# Alternatives

- [FluentResults](https://github.com/altmann/FluentResults)
- [language-ext](https://github.com/louthy/language-ext)