/Monad-CSharp

Monad like programming with C#

Primary LanguageC#GNU General Public License v2.0GPL-2.0

Monad-CSharp

Monad like programming with C#.

An abstract base class Monad that extends IEnumerable, IObservable and IObserver.

Some implementations of the Monad:

  • Maybe
  • Identity
  • List monad
  • CacheMonad
  • (Either is mostly untested, it is some kind of decorator) (Update: currently not working!)

Lots of extension methods (LiftMonad, Comparator functions, easy thread safe function application).

See Doc folder for detailed description an introduction.

Have a look at the Playground.cs for more examples.

A little intro how to use it:

    
    // Creating new maybes using implicit operator overloadings
    Maybe<int> justInt = 5;
    
    // Implicit operator checks value for default(T). 
    // So this becomes a Nothing<int>
    Maybe<int> nothingInt = 0;
    
    var intToDoubleFunction = new Func<int, double>(x => { return x * 0.5; });
    Just<double> justDouble = justInt.Fmap(intToDoubleFunction);
    
    
    // Identity atomic extensions
    Identity<int> intId = 5;
    if(intId.CompareExchange(42, (i) => i % 2 == 1))
    {
        // ....
    }
    ListMonad<int> listMonadInt = new ListMonad<int>() { 1, 2, 3, 4, 5 };
    ListMonad<double> listMonadDouble = new ListMonad<double>() {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
    listMonadInt.Fmap((x) => { return 0.5 * x;});
  
  
    Func<int, double> intDoubleFunc1 = (x) => { return 0.5 * x; };
    Func<int, double> intDoubleFunc2 = (x) => { return 0.7 * x; };
    var lmIntDblFunc = new ListMonad<Func<int, double>>() { intDoubleFunc1, intDoubleFunc2 };
    ListMonad<Double> result = listMonadInt.App(lmIntDblFunc);

Using IObserver/IObservable extensions, and ActionW/MethodW thread safe function application.

    
    Identity<string> id = "string";
    Identity<string> observer = "";
    observer.Disposable = id.Subscribe(observer);
    // or observer.SubscribeAt(id);

    observer.NextAction = (m, x) => Console.WriteLine("Received next: " + x);
    observer.CompleteAction = (m) => Console.WriteLine("Completed, last value: " + m.Return();
    observer.ErrorAction = (e) => Console.WriteLine(e.Message);
    
    // Using Subscribe from reactive extensions
    id.Subscribe((s) => Console.WriteLine("Anonymour Observer Received next: " + s),
                 (e) => Console.WriteLine(e.Message),
                 () => Console.WriteLine("Completed"));

    // Both lines will change the value inside the id monad.
    // The observer monads OnNext will be called.
    
    id.ActionW((i) => i.Pure("1"));
    id.ActionW(() => id.Pure("2"));
         
    public static Func<string, string> setString = (s) => s += "foobar ";
    
    // Adding the string foobar to the current string inside the identity two times,
    // in different ways.
    id.MethodW(() => { return id.Pure(id.Fmap(setString).Return()); });
    id.ActionW(() => id.Pure(id.Fmap(setString).Return()));
            
    id.EndTransmission();   
            
    // Functions for combination with IMonad as result.
    Func<int, double, IMonad<double>> intDblIMonadDblF1 = 
        (x, y) => { return new Just<double>((double)x + y); };
  
    Func<int, double, IMonad<double>> intDblIMonadDblF2 = 
        (x, y) => { return new Just<double>((double)x - y); };
  
    Func<int, double, IMonad<double>> intDblIMonadDblF3 = 
        (x, y) => { return new Just<double>((double)x * y); };
  
    Func<int, double, IMonad<double>> intDblIMonadDblF4 = 
        (x, y) => { return new Just<double>((double)x / y); };
  
    Func<int, double, IMonad<double>> intDblIMonadDblF5 = 
        (x, y) => { return new ListMonad<double>(){(double)x % y}; };
  
    Func<int, double, IMonad<double>> intDblIMonadDblF6 = 
        (x, y) => { return new ListMonad<double>() { (double)x * y * y, (double) x * y * y * y }; };
    
    Func<int, double, IMonad<double>> intDblIMonadDblF7 = 
        (x, y) => { return new Nothing<double>(); };
  
    var listMonadIntDblIMonadDblFunc = new ListMonad<Func<int, double, IMonad<double>>>()
                                            {intDblIMonadDblF1,
                                            intDblIMonadDblF2,
                                            intDblIMonadDblF3,
                                            intDblIMonadDblF4,
                                            intDblIMonadDblF5,
                                            intDblIMonadDblF6,
                                            intDblIMonadDblF7};
                       
    ListMonad<int> listMonadInt = new ListMonad<int>() { 1, 2, 3, 4, 5 };
    ListMonad<double> listMonadDouble = new ListMonad<double>() {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
  
  
    Console.WriteLine("Function applying with Linq: \n" + 
                                " from f in [+, -, *, %]\n" +
                                " from x in [1,..,5]\n" +
                                " from y in [1.0,..,9.0] \n"+
                                " select f(x,y) \n");
                              
    var query = from f in listMonadIntDblIMonadDblFunc
                from x in listMonadInt
                from y in listMonadDouble
                select f(x, y);
    counter = 0;
    // Query is a IMonad again
    query.Visit((x) =>
                {
                    Console.Out.Write(x + ", ");
                    counter++;
                    if (counter % 9 == 0)
                        Console.WriteLine("");
                    if (counter % (5 * 9) == 0)
                        Console.WriteLine("------------------------------------------------");
                          });