dotNext
                                
                                
                                
                                    dotNext copied to clipboard
                            
                            
                            
                        Added convenience methods for easier Monad creation and Monad chaining
I added some convenience methods to the library. The Option class receives a few extension methods that allow for chaining async conversions:
var optional = await Optional.FromValue(42)
    .Convert(async x => x > 10 ? Optional.FromValue(x * 2.0 - 20) : Optional<double>.None)
    .Convert(async x => x > 0 ? Optional.FromValue("Success") : Optional<string>.None);
Console.WriteLine(optional.HasValue ? optional.Value : "Optional has no value");
This is really useful if you work with databases where data is fetched / transformed multiple times asynchronously before being filled into a DTO. The same thing for Result:
var result = await Result.FromValue(42)
    .Convert(async x => x > 10 ? Result.FromValue(x * 2.0 - 20) : Result.FromException<double>(new Exception("x too small")))
    .Convert(async x => x > 0 ? Result.FromValue("Success") : Result.FromException<string>(new Exception("x less than zero")));
Console.WriteLine(result.IsSuccessful ? result : $"Failed with exception: {result.Error}");
This also forwards the exception up the chain just like with regular monads.
There's also overloads for converting Option ↔ Result in both directions if there's need for conversion within a chain.
var optionalFromResult = await Task.FromResult(Result.FromValue<int, TestError>(42))
    .TryGet()
    .Convert(async x => x > 10 ? Optional.FromValue(x) : Optional<int>.None);
Console.WriteLine(optionalFromResult.HasValue ? optionalFromResult.Value : "Optional has no value");
var resultFromOptional = await Task.FromResult(Optional.FromValue(42))
    .ToResult()
    .Convert(async x => x > 10 ? Result.FromValue("Success") : Result.FromException<string>(new Exception("x too small")));
Console.WriteLine(resultFromOptional.IsSuccessful ? resultFromOptional.Value : $"Failed with exception: {resultFromOptional.Error}");
Also, I added some additional convenience methods for creating Options and Results statically:
var optionalWithValue = Optional.FromValue(42);
var errorCodeResultWithValue = Result.FromValue<int, TestError>(42);
var errorCodeResultWithError = Result.FromError<int, TestError>(TestError.Error1);