Skip to content

Instantly share code, notes, and snippets.

@fabiomarreco
Last active November 28, 2019 10:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fabiomarreco/a9af5b466f080662aa22df8a3047975e to your computer and use it in GitHub Desktop.
Save fabiomarreco/a9af5b466f080662aa22df8a3047975e to your computer and use it in GitHub Desktop.
Quick example for a Result Monad
using System;
using System;
using System.Collections;
using System.Collections.Generic;
namespace ResultMonadExample
{
public Result<IBasket> AddToBasketV3(string productId, string customerId)
{
var result =
from product in _productRepository.GetProductId(productId)
from reservation in _inventory.ReserveProduct(product)
from customer in _customerRepository.GetCustomerId(customerId)
select customer.Basket.WithProductReservation(reservation);
return result;
}
public Result<IBasket> AddToBasketV2(string productId, string customerId)
{
var productResult = _productRepository.GetProductId(productId);
var reservationResult = productResult.FlatMap(p => _inventory.ReserveProduct(p));
var customerResult = _customerRepository.GetCustomerId(customerId);
var result = reservationResult
.FlatMap(reservation =>
customerResult.Map(c => c.Basket.WithProductReservation(reservation)));
return result;
}
IProductRepository _productRepository;
IInventory _inventory;
ICustomerRepository _customerRepository;
public class Product
{
}
public class ProductReservation
{
}
public class Customer
{
public IBasket Basket { get; set; }
}
public interface IProductRepository
{
Result<Product> GetProductId(string id);
}
public interface IInventory
{
Result<ProductReservation> ReserveProduct(Product product);
}
public interface IBasket
{
IBasket WithProductReservation(ProductReservation reservation);
}
public interface ICustomerRepository
{
Result<Customer> GetCustomerId(string customerId);
}
public class Result<T>
{
public Result(string errorDescription)
{
IsSuccess = false;
ErrorDescription = errorDescription;
}
public Result(T value)
{
IsSuccess = true;
Value = value;
}
public static Result<T> Error(string description) => new Result<T>(description);
public static Result<T> Success(T result) => new Result<T>(result);
public bool IsSuccess { get; }
public T Value { get; }
public string ErrorDescription { get; }
public Result<T2> Map<T2>(Func<T, T2> fn)
{
if (!IsSuccess)
return Result<T2>.Error(this.ErrorDescription);
var value2 = fn(Value);
return Result<T2>.Success(value2);
}
public Result<T2> FlatMap<T2>(Func<T, Result<T2>> fn)
{
if (!IsSuccess)
return Result<T2>.Error(this.ErrorDescription);
return fn(Value);
}
}
public Result<T> GetItemIndex<T>(T[] array, int index)
{
if (index < 0)
return Result<T>.Error("Index should be greater then zero");
if (index >= array.Length)
return Result<T>.Error("Index is greater then the array size");
return Result<T>.Success(array[index]);
}
public static class Result
{
public static Result<TResult> Select<TSource, TResult>(this Result<TSource> m, Func<TSource, TResult> f)
=> m.Map(f);
public static Result<TResult> SelectMany<TSource, TResult>(this Result<TSource> m, Func<TSource, Result<TResult>> f)
=> m.FlatMap(f);
/*
* This function is required by linq for optimization reasons, you can define it
* in a very mechanical way as bellow
*/
public static Result<TResult> SelectMany<TSource, TM, TResult>(this Result<TSource> m, Func<TSource, Result<TM>> mSelector, Func<TSource, TM, TResult> rSelector)
=> m.FlatMap(v => mSelector(v).Map(tm => rSelector(v, tm)));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment