Skip to content

Instantly share code, notes, and snippets.

@sschoener
Last active April 10, 2024 06:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sschoener/95eb0a532e210c822b2f55e90b07b1a9 to your computer and use it in GitHub Desktop.
Save sschoener/95eb0a532e210c822b2f55e90b07b1a9 to your computer and use it in GitHub Desktop.
A Result class for C# with monadic operations
public struct Failure {
public readonly string Message;
public Failure(string message) {
if (message == null)
Message = "";
else
Message = message;
}
}
public struct Result {
// This is != null iff this is a success.
private readonly string _message;
private Result(string msg) {
_message = msg;
}
public bool IsSuccess { get { return _message == null; } }
public string Message {
get {
if (IsSuccess)
throw new System.InvalidOperationException("no text available");
return _message;
}
}
public Failure Failure {
get {
if (IsSuccess)
throw new System.InvalidOperationException("not a failure");
return new Failure(_message);
}
}
public static Result MakeFailure(string msg) {
if (msg == null)
msg = "";
return new Result(msg);
}
public static Result Success() {
return new Result(null);
}
public static Result<T> Success<T>(T value) {
return Result<T>.Success(value);
}
public static Failure Fail(string msg) {
return new Failure(msg);
}
public static implicit operator Result(Failure f) {
return Result.MakeFailure(f.Message);
}
}
public struct Result<T> {
private readonly T _data;
private readonly string _message;
private Result(T data, string msg) {
_message = msg;
_data = data;
}
public bool IsSuccess { get { return _message == null; } }
public string Message {
get {
if (IsSuccess)
throw new System.InvalidOperationException("no text available");
return _message;
}
}
public T Data {
get {
if (!IsSuccess)
throw new System.InvalidOperationException("not a success");
return _data;
}
}
public Failure Failure {
get {
if (IsSuccess)
throw new System.InvalidOperationException("not a failure");
return new Failure(_message);
}
}
public Result AsResult() {
if (IsSuccess)
return Result.Success();
return Failure;
}
public static Result<T> MakeFailure(string msg) {
if (msg == null)
msg = "";
return new Result<T>(default(T), msg);
}
public static Result<T> Success(T value) {
return new Result<T>(value, null);
}
public static implicit operator Result<T>(Failure f) {
return Result<T>.MakeFailure(f.Message);
}
}
public static class ResultExtensions {
public static void Match(this Result r, Action onSuccess, Action<string> onFailure) {
if (r.IsSuccess)
onSuccess();
else
onFailure(r.Message);
}
public static T Match<T>(this Result r, Func<T> onSuccess, Func<string, T> onFailure) {
if (r.IsSuccess)
return onSuccess();
else
return onFailure(r.Message);
}
public static Result Bind(this Result r, Func<Result> next) {
if (r.IsSuccess)
return next();
return r;
}
public static Result<T> Bind<T>(this Result r, Func<Result<T>> next) {
if (r.IsSuccess)
return r.next();
return Result<T>.MakeFailure(r.Message);
}
public static Result<T> Bind<T>(this Result r, T next) {
if (r.IsSuccess)
return Result<T>.Success(next);
return Result<T>.MakeFailure(r.Message);
}
public static Result<T> Bind<T>(this Result r, Func<T> next) {
if (r.IsSuccess)
return Result<T>.Success(next());
return Result<T>.MakeFailure(r.Message);
}
public void Match<T>(this Result<T> r, Action<T> onSuccess, Action<string> onFailure) {
if (r.IsSuccess)
onSuccess(r.Data);
else
onFailure(r.Message);
}
public TResult Match<T, TResult>(this Result<T> r, Func<T, TResult> onSuccess, Func<string, TResult> onFailure) {
if (r.IsSuccess)
return onSuccess(r.Data);
else
return onFailure(r.Message);
}
public Result<R> Map<T, R>(this Result<T> r, Func<T, R> f) {
if (r.IsSuccess)
return Result<R>.Success(f(r.Data));
return Result<R>.MakeFailure(r.Message);
}
public Result<TResult> Bind<T, TResult>(this Result<T> r, Func<T, Result<TResult>> next) {
if (r.IsSuccess)
return next(r.Data);
return Result<TResult>.MakeFailure(r.Message);
}
public Result<T> Bind(this Result<T> r, Func<T, Result> next) {
if (r.IsSuccess)
return next(r.Data).Bind(r.Data);
return r;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment