Created
February 3, 2020 02:34
-
-
Save dbramucci/d393a22612c1981c5f8069d640e17205 to your computer and use it in GitHub Desktop.
C# Result Proof of Concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading; | |
// S is the type stored for successful computations, E for errors | |
public struct Result<S, E> | |
{ | |
private bool isSuccessful; | |
private S successValue; | |
private E errorValue; | |
// User should only use Result<S, E>.Error and Result<S, E>.Success | |
// to construct Result objects | |
private Result(bool isSuccessful, S successValue, E errorValue) | |
{ | |
this.isSuccessful = isSuccessful; | |
this.successValue = successValue; | |
this.errorValue = errorValue; | |
} | |
// Construct a Result representing an Error | |
public static Result<S, E> Error(E errorValue) | |
{ | |
return new Result<S, E>(false, default (S), errorValue); | |
} | |
// Construct a Result representing a Success | |
public static Result<S, E> Success(S successValue) | |
{ | |
return new Result<S, E>(true, successValue, default (E)); | |
} | |
// Construct a value by telling the Result object how to make your | |
// Value when there was a success and how to make the value | |
// If an error happened | |
public R Match<R>(Func<S, R> whenSuccess, Func<E, R> whenError) | |
{ | |
if (isSuccessful) | |
{ | |
return whenSuccess(successValue); | |
} | |
else | |
{ | |
return whenError(errorValue); | |
} | |
} | |
// Extract the successful value, using a provided value in case the | |
// Result is an error. | |
public S FromDefault(S default_) | |
{ | |
return Match(s => s, e => default_); | |
} | |
// Perform an action for the Success and Error cases | |
// Without returning a value, this is useful when you | |
// Don't want to get a result from the Match. | |
public void Do(Action<S> whenSuccess, Action<E> whenError) | |
{ | |
if (isSuccessful) | |
{ | |
whenSuccess(successValue); | |
} | |
else | |
{ | |
whenError(errorValue); | |
} | |
} | |
// Apply a function only if the Result is a success | |
public Result<T, E> MapSuccess<T>(Func<S, T> f) | |
{ | |
return Match(s => Result<T, E>.Success(f(s)), e => Result<T, E>.Error(e)); | |
} | |
// Apply a function only if the Result is an error | |
public Result<S, F> MapError<F>(Func<E, F> f) | |
{ | |
return Match(s => Result<S, F>.Success(s), e => Result<S, F>.Error(f(e))); | |
} | |
// Apply functions depending on what the Result is, storing creating a new Result | |
public Result<T, F> BiMap<T, F>(Func<S, T> whenSuccess, Func<E, F> whenError) | |
{ | |
return Match(s => Result<T, F>.Success(whenSuccess(s)), | |
e => Result<T, F>.Error(whenError(e))); | |
} | |
// If the first Result is an error, use its error | |
// Otherwise return the second results success/error | |
public Result<T, E> And<T>(Result<T, E> result) | |
{ | |
return AndThen(_ => result); | |
} | |
// If the first Result is an Error return that, | |
// Otherwise call the provided function on the successful value | |
// and return the result of that. | |
public Result<T, E> AndThen<T>(Func<S, Result<T, E>> f) | |
{ | |
return Match(s => f(s), | |
e => Result<T, E>.Error(e)); | |
} | |
} | |
class MainClass { | |
public static Result<int, string> SafeDivide(int n, int d) | |
{ | |
if (d == 0) | |
{ | |
return Result<int, string>.Error($"Zero Division: n/d = {n}/{d}"); | |
} | |
else | |
{ | |
return Result<int, string>.Success(n/d); | |
} | |
} | |
public static void Main (string[] args) { | |
Console.WriteLine ( | |
Result<String, String> | |
.Error("Hello World") | |
.Match(s => { | |
var temp = "win: "; | |
temp += s; | |
return temp; | |
}, | |
e => $"fail: {e}") | |
); | |
Result<String, String> | |
.Success("hello world there lower") | |
.BiMap(Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase, | |
Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase) | |
.Do(s => Console.WriteLine("win: " + s), | |
e => {}); | |
Console.WriteLine( | |
SafeDivide(5618, 25) | |
.AndThen(s => SafeDivide(s, 4)) | |
.MapSuccess(s => s * 2345) | |
.AndThen(s => SafeDivide(s, 0)) | |
.MapSuccess(s => s * 4327) | |
.MapSuccess(s => s + 42) | |
.Match(s => $"Success: {s}", | |
e => $"Error: {e}") | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment