title: Monad Transformers in C#
body: <p>I am working on using monad transformers in C#.<br>
  I would like to know if the following code I present, shows that I have understood this.<br>
  I am fairly new to this so any feedback / comments are really welcome.<br>
  This example is just for wrapping a maybe monad in a validation monad.</p>
  
  <pre><code>using System;
  using NUnit.Framework;
  
  namespace Monads
  {
      public static class MaybeExtensions
      {
          public static IMaybe&lt;T&gt; ToMaybe&lt;T&gt;(this T value)
          {
              if (value == null)
                  return new None&lt;T&gt;();
  
              return new Just&lt;T&gt;(value);
          }
      }
  
      public interface IMaybe&lt;T&gt;
      {
          IMaybe&lt;U&gt; Select&lt;U&gt;(Func&lt;T, U&gt; f);
  
          IMaybe&lt;U&gt; SelectMany&lt;U&gt;(Func&lt;T, IMaybe&lt;U&gt;&gt; f);
  
          U Fold&lt;U&gt;(Func&lt;U&gt; error, Func&lt;T, U&gt; success);
      }
  
      public class Just&lt;T&gt; : IMaybe&lt;T&gt;
      {
          public Just(T value)
          {
              this.value = value;
  
          }
  
          public IMaybe&lt;U&gt; Select&lt;U&gt;(Func&lt;T, U&gt; f)
          {
              return f(value).ToMaybe();
          }
  
          public IMaybe&lt;U&gt; SelectMany&lt;U&gt;(Func&lt;T, IMaybe&lt;U&gt;&gt; f)
          {
              return f(value);
          }
  
          public U Fold&lt;U&gt;(Func&lt;U&gt; error, Func&lt;T, U&gt; success)
          {
              return success(value);
          }
  
          public IValidation&lt;U, T&gt; ToValidationT&lt;U&gt;()
          {
              return new ValidationMaybeT&lt;U, T&gt;(this, default(U));
          }
  
          private readonly T value;
      }
  
      public class None&lt;T&gt; : IMaybe&lt;T&gt;
      {
          public IMaybe&lt;U&gt; Select&lt;U&gt;(Func&lt;T, U&gt; f)
          {
              return new None&lt;U&gt;();
          }
  
          public IMaybe&lt;U&gt; SelectMany&lt;U&gt;(Func&lt;T, IMaybe&lt;U&gt;&gt; f)
          {
              return new None&lt;U&gt;();
          }
  
          public U Fold&lt;U&gt;(Func&lt;U&gt; error, Func&lt;T, U&gt; success)
          {
              return error();
          }
  
          public IValidation&lt;U, T&gt; ToValidationT&lt;U&gt;(U exceptionalValue)
          {
              return new ValidationMaybeT&lt;U, T&gt;(this, exceptionalValue);
          }
      }
  
      public class Customer
      {
          public Customer(string name)
          {
              Name = name;
          }
  
          public string Name { get; set; }
      }
  
      public interface IValidation&lt;T, U&gt;
      {
          IValidation&lt;T, V&gt; Select&lt;V&gt;(Func&lt;U, V&gt; f);
  
          IValidation&lt;T, V&gt; SelectMany&lt;V&gt;(Func&lt;U, IValidation&lt;T, V&gt;&gt; f);
      }
  
      public class ValidationError&lt;T, U&gt; : IValidation&lt;T, U&gt;
      {
          public ValidationError(T error)
          {
              Error = error;
          }
  
          public IValidation&lt;T, V&gt; Select&lt;V&gt;(Func&lt;U, V&gt; f)
          {
              return new ValidationError&lt;T, V&gt;(Error);
          }
  
          public IValidation&lt;T, V&gt; SelectMany&lt;V&gt;(Func&lt;U, IValidation&lt;T, V&gt;&gt; f)
          {
              return new ValidationError&lt;T, V&gt;(Error);
          }
  
          public T Error { get; private set; }
      }
  
      public class ValidationSuccess&lt;T, U&gt; : IValidation&lt;T, U&gt;
      {
          public ValidationSuccess(U value)
          {
              Result = value;
          }
  
          public IValidation&lt;T, V&gt; Select&lt;V&gt;(Func&lt;U, V&gt; f)
          {
              return new ValidationSuccess&lt;T, V&gt;(f(Result));
          }
  
          public IValidation&lt;T, V&gt; SelectMany&lt;V&gt;(Func&lt;U, IValidation&lt;T, V&gt;&gt; f)
          {
              return f(Result);
          }
  
          public U Result { get; private set; }
      }
  
      public class ValidationMaybeT&lt;T, U&gt; : IValidation&lt;T, U&gt;
      {
          public ValidationMaybeT(IMaybe&lt;U&gt; value, T error)
          {
              Value = value;
              Error = error;
          }
  
          public IValidation&lt;T, V&gt; Select&lt;V&gt;(Func&lt;U, V&gt; f)
          {
              return Value.Fold&lt;IValidation&lt;T, V&gt;&gt;(() =&gt; new ValidationError&lt;T, V&gt;(Error), s =&gt; new ValidationSuccess&lt;T, V&gt;(f(s)));
          }
  
          ValidationError&lt;T, V&gt; SelectManyError&lt;V&gt;()
          {
              return new ValidationError&lt;T, V&gt;(Error);
          }
  
          public IValidation&lt;T, V&gt; SelectMany&lt;V&gt;(Func&lt;U, IValidation&lt;T, V&gt;&gt; f)
          {
              return Value.Fold(() =&gt; SelectManyError&lt;V&gt;(), s =&gt; f(s));
          }
  
          public IMaybe&lt;U&gt; Value { get; private set; }
  
          public T Error { get; private set; }
      }
  
      public interface ICustomerRepository
      {
          IValidation&lt;Exception, Customer&gt; GetById(int id);
      }
  
      public class CustomerRepository : ICustomerRepository
      {
          public IValidation&lt;Exception, Customer&gt; GetById(int id)
          {
  
              if (id &lt; 0)
                  return new None&lt;Customer&gt;().ToValidationT&lt;Exception&gt;(new Exception("Customer Id less than zero"));
  
              return new Just&lt;Customer&gt;(new Customer("Structerre")).ToValidationT&lt;Exception&gt;();
          }
      }
  
      public interface ICustomerService
      {
          void Delete(int id);
      }
  
      public class CustomerService : ICustomerService
      {
          public CustomerService(ICustomerRepository customerRepository)
          {
              this.customerRepository = customerRepository;
  
          }
  
          public void Delete(int id)
          {
              customerRepository.GetById(id)
                  .SelectMany(x =&gt; SendEmail(x).SelectMany(y =&gt; LogResult(y)));
  
  
          }
  
          public IValidation&lt;Exception, Customer&gt; LogResult(Customer c)
          {
              Console.WriteLine("Deleting: " + c.Name);
              return new ValidationSuccess&lt;Exception, Customer&gt;(c);
              //return new ValidationError&lt;Exception, Customer&gt;(new Exception("Unable write log"));
          }
  
          private IValidation&lt;Exception, Customer&gt; SendEmail(Customer c)
          {
              Console.WriteLine("Emailing: " + c.Name);
              return new ValidationSuccess&lt;Exception, Customer&gt;(c);
          }
  
          ICustomerRepository customerRepository;
      }
  
      [TestFixture]
      public class MonadTests
      {
          [Test]
          public void Testing_With_Maybe_Monad()
          {
              new CustomerService(new CustomerRepository()).Delete(-1);
          }
      }
  }
  </code></pre>
  
  <p>Another smaller sub question is if C# had higher kinded types could I just implement this class once (ValidationT) and it work for all other wrapped monads or is this incorrect?</p>