Introduction
A few months ago I had lesson about function programming in Haskell. Since then I was getting more and more into the function way of thinking.
Currently im programming C# alot (Private, at work). And I started to use the functional things like lambdas and delegates more and more.
I was wondering if I can get something like the Monads and (Applicative)Functors in Haskell. And I simply wanted to play around with C#, functional programming and generics.
I ended up with some realy cool stuff I would like to share.
The things I found at the web when I was googling about C# and monads where about functional programming
some isolated Maybe or Identity implementations or some extensions of IEnumerable
. But I had something more general in mind.
At the beginning I
didn't know much about IEnumerable
and the implementation details. I only knew Linq, and that it was established by a (legendary) haskell´er with ideas about monads in mind.
But still I found some usefull or at least complex function definitions that are not in the IEnumerable interface. And I connected all Monads to the
IEnumerable
and LINQ syntax.
(for..in..., where, select).
Basically in the IEnumerable interface, there are only functions that take one function as an argument. There is no function that takes a List
with functions for example. Maybe that was not the intention of IEnumerable
when they made it. Or it is not really needed because normally someone uses the Linq syntax (for..in..)
and doesnt call the functions directly.
The complete project with more detailed descriptions can be found under github:
https://github.com/Muraad/Monad-CSharp
What you will see?
A lot of generics and delegates/Func/lambdas. Implementing IEnumerable and make connection to LINQ. And operator overloading.
What I have done:
I´v defined an interface IMonad that extends IEnumerable, and adds some usefull and complex function over monads. Functions I dont find in the IEnumerable.
And the big difference is IEnumerable is normally only for list like types. And not for types with a single value inside.
So I've defined that interface and implemented a Maybe,
Identity, ListMonad and Either monad.
Background
What is a Monad and a (Applicative)Functor?
Basically its all the same.
It's like a box that can have a value ore more inside. And things can be done with this value. For example a function can be mapped over it. It can be zipped together with the value(s)
inside another box. The list (or better IEnumerable) is the best example for a monad, because everybody know this is a type (a box) that can have other values inside (this box).
And you can define functions for a list without to know something about the types inside the list.
What is a Maybe?
A Maybe<T> is a class (box) that can have one value of type T inside or it can be nothing. Its like the nullable in C#.
Using the code
I made an IMonad<A> interface and some implementations of it. A Maybe<A>, Identity<A>, ListMonad<A> and an Either<R, L>.
I will explain only the IMonad here, and give some examples how to use the Maybe and ListMonad classes.
IMonad Interface
public interface IMonad<A> : IEnumerable<A>
{
#region IMonad_Core_Interface_Function_Definitions
IMonad<B> Fmap<B>(Func<A, B> function);
IMonad<A> Pure(A parameter);
A Return();
IMonad<B> App<B>(IMonad<Func<A, B>> functionMonad);
IMonad<B> App<B>(IMonad<Func<A, IMonad<B>>> functionMonad);
IMonad<C> Com<B, C>(Func<A, B, C> function, IMonad<B> mOther);
IMonad<C> Com<B, C>(Func<A, B, IMonad<C>> function, IMonad<B> mOther);
IMonad<C> Com<B, C>(IMonad<Func<A, B,
C>> functionMonad, IMonad<B> mOther);
IMonad<C> Com<B, C>(IMonad<Func<A, B,
IMonad<C>>> functionMonad, IMonad<B> mOther);
IMonad<A> Visit(Action<A> function);
IMonad<A> Visit<B>(Action<A, B> action, IMonad<B> mOther);
IMonad<A> Concat(IMonad<A> otherMonad);
#endregion
#region Linq_Enumerable_Connection
IMonad<A> Where(Func<A, bool> predicate); IMonad<A> Where(Func<A, int, bool> predicate);
IMonad<B> Select<B>(Func<A, B> f); IMonad<B> Select<B>(Func<A, Int32, B> f);
IMonad<B> SelectMany<B>(Func<A, IMonad<B>> f);
IMonad<B> SelectMany<B>(Func<A, Int32, IMonad<B>> f);
IMonad<B> SelectMany<TMonad, B>(Func<A, IMonad<TMonad>> selector,
Func<A, TMonad, B> function);
IMonad<B> SelectMany<TMonad, B>(Func<A, Int32, IMonad<TMonad>> selector,
Func<A, TMonad, B> function);
#endregion
}
IMonad extends IEnumerable, that means every monad is an enumerable even if it holds only one value. I found out that is is very usefull for example
for the list monad App and Com functions. Because then foreach can be used on the other monad no matter if it holds only one value or more. The IMonad interface
also defines LINQ functions, that every Monad has to implement. When I was playing around and doing some research I recognized that IEnumerable is almost a Monad.
But because an Applicative Functor is a monad like type too, I saw that there is no "App" like function defined in IEnumerable. And IEnumerable normaly
is for types like collections that holds one ore MORE values. So my IMonad extends IEnumerable. Onyl difference is that some function definitions are "overridden",
that they take IMonad´s as arguments and return values instead of IEnumerable´s.
With the Haskell type classes in front of me, I was starting to design
this interface. There were a few changes over time. For example the IEnumerable extension was not there from the beginning on.
Function explanations:
Pure
IMonad<A> Pure(A parameter);
This function simply takes a value and puts it in the minimal context of the (new created) monad.
Return
A Return();
Returns the value inside this monad. A problem here is the ListMonad. Because it holds more than one values of type A.
The workaround I do is to return the head of the internal list. Another problem here is with Maybe. What to do if someone ask a maybe for its value inside and it is nothing?
As a workaround I return the default value of the type inside the maybe. For example for Nothing<int> the Return() function will return zero. Either is also a problem here. My Either implementation is basically a decorator and implemented like Either<R,L> : Identity<L>, IMonad<R>. For the left value functions are delegated to the base Identity class. The IMonad<R> is implemented in Either and is the same as the Identity implementation. Based on the input arguments and the generic parameters C# can choose the right function for every function defined in IMonad except for Return().
Fmap
IMonad<B> Fmap<B>(Func<A, B> function);
Basically fmap is the same as the simple Select from IEnumerable. It maps a function over the value(s) inside this monad. The result is packed from fmap into a new monad that is returned. I was starting to write the IMonad interace before I was getting more into LINQ and recognized that there is alread the Select. But I let it inside the interface because I think fmap is a better and more understable name here. In Haskell its called fmap too, and is defined in the functor typeclass.
Fmap 2nd
IMonad<B> Fmap<B>(Func<A, IMonad<B>> function);
Same as Fmap above. Difference is that the function returns a new monad.
App
IMonad<B> App<B>(IMonad<Func<A, B>> functionMonad);
This is an interesting function definition. It comes from the Haskell Applicative typeclass. Thats the function I dont find in IEnumerable. It is like the fmap function, the only difference is that the function that is applied to the value(s) inside the monad, is also inside another monad. So you can have a List(Monad) with functions and apply them to all values in another list(monad) with one line. Or you can have a function that is packed in a maybe and apply them to the value(s) inside of this monad. The result of applying the function(s) to the value(s) inside the monad is packed into a new monad that is returned.
App 2nd
IMonad<B> App<B>(IMonad<Func<A, IMonad<B>>> functionMonad);
The second version of the App function (like its defined in Haskell) is even more interesting. Here the function(s) inside the given monad also returns a monad. So the result is flattened out if there are more than one result values. If the function returns a list(monad) of type B and this monad is a list(monad) of type A then the result type is also a list(monad) of type B and not a list(monad) of list(monad) of type B. The inner monad is always flattened out. This function is something special. It can "break out" of the monad type that function is called from. That means App on a Maybe can return a ListMonad for example. At first it seems there is a problem. How to concatenate the values inside the function result monads of type B together? If you are inside aListMonad that would be no problem. But what about a Maybe that can only store one value? The trick here is to use the firstIMonad that is returned from the first function. All other values inside the next returned monads are concatenated together. Thats why there is a Concat function in the IMonad interface. This is a pattern I will use in all functions, that work with new monads as
internal function results. What Concat means for a individual monad is self defined. For a ListMonad it is what someone would expect. For a Maybe for example, I simply return a new Maybe with the value inside that is returned from the Return() function from the given Monad.
Com
IMonad<C> ZipWith<B, C>(Func<A, B, C> function, IMonad<B> mOther);
Put every possible combination of the values inside this ListMonad and the other given IMonad as arguments inside the given function. All function results are put into a new monad.
Com 2nd
IMonad<C> Com<B, C>(Func<A, B, IMonad<C>> function, IMonad<B> mOther);
Same as above only difference is that the function itselfs return IMonad. So the result IMonad is flattned out, and every value inside of it is added to the new result monad.
Com 3d
IMonad<C> Com<B, C>(IMonad<Func<A, B, C>> functionMonad, IMonad<B> mOther);
Same as above, where the given function(s) is(are) inside a IMonad.
Com 4th
IMonad<C> Com<B, C>(IMonad<Func<A, B, IMonad<C>>> functionMonad, IMonad<B> mOther);
Same as Com 2d only that the functions inside the monad returns a monad them self. The same procedere as used in App is used to produce the result monad. This is the most complex function.
Visit
IMonad<A> Visit(Action<A> function);
Apply the given Action to the value(s) inside of this monad. During my testing I found that function very usefull. For example you can apply a lambda with Console.Out.Write... over the value(s) inside the monad.
Visit 2nd
IMonad<A> Visit<B>(Action<A, B> action, IMonad<B> mOther);
Visit each combination of the values inside this monad and the other given monad.
Concat
IMonad<A> Concat(IMonad<A> otherMonad);
Concatenates two monads together. There is no general defenition here what concat means for a concrete monad.
The rest of the function definitions are for the connection to LINQ. That every monad can be used with Linq. What would already possible because IMonad extends IEnumerable. And as long as every monad has a Enumerator, there could be Linq querys on it. But I want that the Linq functions return and work with IMonad´s.
Linq connection
Same defintions like in IEnumerable only IEnumerable is replaced with IMonad. So Linq and Monad functions can be mixed up.
Something to mention here is the case where a Linq function takes a function as argument that uses an index of the current value inside the IEnumerable. For single value monads like Maybe or Identity the index is always zero.
Single and multi value monads
There are monads that hold only one value (single enumerable) like maybe and identity, and there are monads that hold one or more like a list monad (or every normal enumerable or a collection).
Breaking out the the current monad type
Ever function where a Func<...,IMonad<..>> is involved, has the ability to "break out" of the current monad type, that means the result monad type can be different than the monad where Func was used. All other functions return a monad of the same type.
Convention
No monad knows another monad. They all only know IMonad and them self. So there is no special handling between different combination of monads. To change the result monad even if its unknow, a trick described at function App 2nd is used. In short theIMonad that is returned from the first given function is used to concatenate with the next function results.
Maybe playground
public static void MaybePlayaround()
{
Maybe<int> justInt = 5;
Console.WriteLine("A new Just<double>: " + justInt.ToString());
Maybe<int> nothingInt = 0; Console.WriteLine("A new Nothing<double>: " + nothingInt.ToString());
Console.WriteLine("A new ListMonad<char>: ");
var listMonadChar = new ListMonad<char>() { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }
.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.WriteLine("A new ListMonad<int>: ");
var listMonadInt = new ListMonad<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.WriteLine("A new ListMonad<double>: ");
var listMonadDouble = new ListMonad<double>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
var intToDoubleFunctin = new Func<int, double>(x => { return x * 0.5; });
Console.WriteLine("A new Just with a function inside: f(x) = x * 0.5 ");
var justFunction = new Just<Func<int, double>>(intToDoubleFunctin);
Console.WriteLine(justFunction.ToString());
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Visits each combination of the Just 5 and the ListMonad<char>" +
"using a lambda and Console.Write inside: ");
justInt.Visit((i, c) => { Console.Out.Write(i + "" + c + ", "); }, listMonadChar);
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Same with Nothing<int> will output nothing: ");
nothingInt.Visit((i, c) => { Console.Out.Write(i + "" + c + ", "); }, listMonadChar);
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Visits each combination of the Just 5 and the ListMonad<int> \n" +
"using a lambda and Console.Write inside. Add both values in print out: ");
justInt.Visit((x, y) => { Console.Out.Write( x + y + ", "); }, listMonadInt);
Console.WriteLine("\nSame with Nothing<int>:");
nothingInt = (Maybe<int>)nothingInt
.Visit((x, y) => { Console.Out.Write(x + y + ", "); }, listMonadInt);
Console.WriteLine(nothingInt.ToString());
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.Write("Fmap f(x) = x * 0.5 over the Just<int>(5): ");
var justDouble = justInt.Fmap(intToDoubleFunctin).Visit((x) => { Console.Out.Write(x + "\n"); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.Write("App Just<Func>(f(x) = x * 0.5) over the Just<int>(5): ");
justDouble = justInt.App(justFunction).Visit((x) => { Console.Out.Write(x + "\n"); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.Write("App Just<Func> over the Just<int>(5), \n where the functions returns a new " +
"ListMonad<int>() \n with two times the value inside the Just 5. Output: ");
var function = new Just<Func<int, IMonad<int>>>((x) => { return new ListMonad<int>(){x, x};});
var intListMonad = justInt.App(function).Visit( (x) => { Console.Out.Write(x + ", "); } );
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Create a new ListMonad with" +
" Func<int, int, double> (x*y, x/y, x%y) inside.");
Console.WriteLine("Combinate Just 5 and that functions. Result is Just<int>.");
Console.WriteLine("Only last value is returned because this" +
" Com function cannot break out of the Just.");
Console.WriteLine();
var functionListMonad = new ListMonad<Func<int, int, double>>();
functionListMonad.Add( (x, y) => { return x*y;});
functionListMonad.Add( (x, y) => { return x/ (y==0 ? 1 : y);});
functionListMonad.Add((x, y) => { return x % (y == 0 ? 1 : y); });
functionListMonad.Visit((x) => { Console.Out.WriteLine("Func: " + x); });
var result = justInt.Com(functionListMonad, listMonadInt).Visit((x) =>
{ Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Create a new ListMonad with \n" +
"Func<int, int, IMonad<double>> (x+y, x-y, x*y, x/y, x%y) inside.\n" +
"Where every function packs the result in a new ListMonad<double>.\n" +
"Combine the Just 5 and the ListMonad<double> with all the functions.\n" +
"The result ListMonad´s are flattned out, and only one result ListMonad<double> "+
" with all result values is returned: ");
Console.WriteLine();
var functionListMonadTwo = new ListMonad<Func<int, double, IMonad<double>>>();
functionListMonadTwo.Add((x, y) => { return new ListMonad<double>() { x + y }; });
functionListMonadTwo.Add((x, y) => { return new ListMonad<double>() { x - y }; });
functionListMonadTwo.Add((x, y) => { return new ListMonad<double>(){x * y}; });
functionListMonadTwo.Add((x, y) => { return new ListMonad<double>(){x / (y == 0 ? 1 : y)}; });
functionListMonadTwo.Add((x, y) => { return new ListMonad<double>(){x % (y == 0 ? 1 : y)}; });
functionListMonadTwo.Add((x, y) => { return new Nothing<double>(); });
var resultTwo = justInt.Com(functionListMonadTwo, listMonadDouble)
.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Do the same with the Nothing<int>: ");
resultTwo = nothingInt.Com(functionListMonadTwo, listMonadDouble)
.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.Write("Comb. J(5) and the ListMonad<int> with one function ( f(x,y) = x+y ): ");
var resultThree = justInt.Com((x, y) => { return x + y; }, intListMonad)
.Visit((x) => { Console.Out.Write(x); });
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.Write("Map a f(x, y) = x*y over the Just 5 and a new Just<int>(10) using LINQ: ");
var query = from f in new Just<Func<int, int, int>>((x, y) => { return x * y; })
from x in justInt
from y in new Just<int>(10)
select f(x, y);
query.Visit((x) => { Console.Out.Write(x + ", "); });
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
}
ListMonad playground
public static void ListMonadPlayground()
{
Console.Out.WriteLine("Create two lists [1..5] and [J(1)..(J5)]: ");
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};
ListMonad<Maybe<int>> listMonadMaybeInt = new ListMonad<Maybe<int>>()
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Func<int, double> intDoubleFunc1 = (x) => { return 0.5 * (double)x; };
Func<int, double> intDoubleFunc2 = (x) => { return 0.7 * (double)x; };
Console.WriteLine("Fmap f(x) = 0.5 * x over [1,..5,]");
listMonadInt.Fmap(intDoubleFunc1).Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("App [f(x)=0.5*x, f(x)=0.7*x] over [1,..,5]");
var listMonadintDoubleFunc =
new ListMonad<Func<int, double>>(){ intDoubleFunc1, intDoubleFunc2 };
listMonadInt.App(listMonadintDoubleFunc).Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Func<int, IMonad<double>> intIMonadIntDoubleFunc1 =
(x) => { return new Just<double>(x * x); };
Func<int, IMonad<double>> intIMonadIntDoubleFunc2 =
(x) => { return new Just<double>(x * x *x); };
Func<int, IMonad<double>> intIMonadIntDoubleFunc3 =
(x) => { return new Just<double>(x * x * x * x); };
Func<int, IMonad<double>> intIMonadIntDoubleFunc4 =
(x) => { return new Just<double>(x * x * x * x * x); };
Func<int, IMonad<double>> intIMonadIntDoubleFunc5 =
(x) => { return new ListMonad<double>(){x+1, x-1}; };
var listMonadIMonadIntDoubleFunc = new ListMonad<Func<int, IMonad<double>>>();
listMonadIMonadIntDoubleFunc.Add(intIMonadIntDoubleFunc1);
listMonadIMonadIntDoubleFunc.Add(intIMonadIntDoubleFunc2);
listMonadIMonadIntDoubleFunc.Add(intIMonadIntDoubleFunc3);
listMonadIMonadIntDoubleFunc.Add(intIMonadIntDoubleFunc4);
listMonadIMonadIntDoubleFunc.Add(intIMonadIntDoubleFunc5);
Console.WriteLine("App [Just(x^2), Just(x^3), Just(x^4), Just(x^5] over [1,..,5]");
listMonadInt.App(listMonadIMonadIntDoubleFunc).Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Func<int, double, double> intDoubleDoubleFunc1 =
(x, y) => { return (double)x + y; };
Func<int, double, double> intDoubleDoubleFunc2 =
(x, y) => { return (double)x - y; };
Func<int, double, double> intDoubleDoubleFunc3 =
(x, y) => { return (double)x * y; };
Func<int, double, double> intDoubleDoubleFunc4 =
(x, y) => { return (double)x / y; };
Func<int, double, double> intDoubleDoubleFunc5 =
(x, y) => { return (double)x % y; };
var listMonadIntDoubleDoubleFunc = new ListMonad<Func<int, double, double>>()
{intDoubleDoubleFunc1,
intDoubleDoubleFunc2,
intDoubleDoubleFunc3,
intDoubleDoubleFunc4,
intDoubleDoubleFunc5};
Console.WriteLine("Combination with 'normal' result value and function + :");
listMonadInt.Com(intDoubleDoubleFunc1, listMonadDouble)
.Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Comb. with 'normal' result value and functions [+, -, *, /, %]: ");
listMonadInt.Com(listMonadIntDoubleDoubleFunc, listMonadDouble)
.Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc1 =
(x, y) => { return new Just<double>((double)x + y); };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc2 =
(x, y) => { return new Just<double>((double)x - y); };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc3 =
(x, y) => { return new Just<double>((double)x * y); };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc4 =
(x, y) => { return new Just<double>((double)x / y); };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc5 =
(x, y) => { return new ListMonad<double>(){(double)x % y}; };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc6 =
(x, y) => { return new ListMonad<double>() { (double)x * y * y, (double) x * y * y * y }; };
Func<int, double, IMonad<double>> intDoubleIMonadDoubleFunc7 =
(x, y) => { return new Nothing<double>(); };
var listMonadIntDoubleIMonadDoubleFunc = new ListMonad<Func<int, double, IMonad<double>>>()
{intDoubleIMonadDoubleFunc1,
intDoubleIMonadDoubleFunc2,
intDoubleIMonadDoubleFunc3,
intDoubleIMonadDoubleFunc4,
intDoubleIMonadDoubleFunc5,
intDoubleIMonadDoubleFunc6,
intDoubleIMonadDoubleFunc7};
Console.WriteLine("Combination with IMonad function results.");
Console.WriteLine("List1[1,..,5], List2[1.0,..,9.0] and function +");
listMonadInt.Com(intDoubleIMonadDoubleFunc1, listMonadDouble)
.Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Combination with IMonad function results.");
Console.WriteLine("List1[1,..,5], List2[1.0,..,9.0] and " +
"functions [+, -, *, /, %, [x*y*y, x*y*y*y], Nothing]");
listMonadInt.Com(listMonadIntDoubleIMonadDoubleFunc, listMonadDouble)
.Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Visit with other IMonad and add (+) values in output.");
listMonadInt.Visit<double>((x, y) => { Console.Write(x * y + ", "); }, listMonadDouble);
Console.WriteLine("___________________________________________________________");
Console.ReadLine();
Console.WriteLine("Function applying with Linq: ");
var query = from f in listMonadIntDoubleDoubleFunc
from x in listMonadInt
from y in listMonadDouble
select f(x, y);
query.Visit((x) => { Console.Write(x + ", "); });
Console.WriteLine("");
Console.ReadLine();
Console.WriteLine("Function applying with Linq: ");
var query2 = from f in listMonadIntDoubleIMonadDoubleFunc
from x in listMonadInt
from y in listMonadDouble
select f(x, y);
query2.Visit((x) => { Console.Write(x.ToString() + ", "); });
Console.WriteLine("\n");
Console.ReadLine();
}
Operator overloading playground
I'd done a few operator overloadings for ListMonad.
Now fma, app, combine and concat functions can be used via operators!
public static void ListMonadOperatorPlayground()
{
int counter = 0;
Console.Out.WriteLine("Create two lists [0..9]: ");
ListMonad<double> listMonadDouble = new ListMonad<double>()
{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
ListMonad<double> listMonadDoubleTwo = new ListMonad<double>()
{ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
Func<double, IMonad<double>> doubleIMonadDoubleFun1 =
(x) => { return new Just<double>(x * x); };
Func<double, IMonad<double>> doubleIMonadDoubleFun2 =
(x) => { return new Just<double>(x * x * x); };
Func<double, IMonad<double>> doubleIMonadDoubleFun3 =
(x) => { return new Just<double>(x * x * x * x); };
Func<double, IMonad<double>> doubleIMonadDoubleFun4 =
(x) => { return new Just<double>(x * x * x * x * x); };
Func<double, IMonad<double>> doubleIMonadDoubleFun5 =
(x) => { return new ListMonad<double>() { x + 1, x - 1 }; };
var listMonadFunc1 = new ListMonad<Func<double, IMonad<double>>>();
listMonadFunc1.Add(doubleIMonadDoubleFun1);
listMonadFunc1.Add(doubleIMonadDoubleFun2);
listMonadFunc1.Add(doubleIMonadDoubleFun3);
listMonadFunc1.Add(doubleIMonadDoubleFun4);
listMonadFunc1.Add(doubleIMonadDoubleFun5);
Func<double, double, double> doubleDoubleDoubleFunc1 =
(x, y) => { return (x + y); };
Func<double, double, double> doubleDoubleDoubleFunc2 =
(x, y) => { return x - y; };
Func<double, double, double> doubleDoubleDoubleFunc3 =
(x, y) => { return x * y; };
Func<double, double, double> doubleDoubleDoubleFunc14 =
(x, y) => { return x / y; };
Func<double, double, double> doubleDoubleDoubleFunc5 =
(x, y) => { return x % y; };
var listMonadFunc2 = new ListMonad<Func<double, double, double>>()
{doubleDoubleDoubleFunc1,
doubleDoubleDoubleFunc2,
doubleDoubleDoubleFunc3,
doubleDoubleDoubleFunc14,
doubleDoubleDoubleFunc5};
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc1 =
(x, y) => { return new Just<double>(x + y); };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc2 =
(x, y) => { return new Just<double>(x - y); };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc3 =
(x, y) => { return new Just<double>(x * y); };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc4 =
(x, y) => { return new Just<double>(x / y); };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc5 =
(x, y) => { return new ListMonad<double>() { x % y }; };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc6 =
(x, y) => { return new ListMonad<double>() { x * y * y, x * y * y * y }; };
Func<double, double, IMonad<double>> intDoubleIMonadDoubleFunc7 =
(x, y) => { return new Nothing<double>(); };
var listMonadFunc3 = new ListMonad<Func<double, double, IMonad<double>>>()
{intDoubleIMonadDoubleFunc1,
intDoubleIMonadDoubleFunc2,
intDoubleIMonadDoubleFunc3,
intDoubleIMonadDoubleFunc4,
intDoubleIMonadDoubleFunc5,
intDoubleIMonadDoubleFunc6,
intDoubleIMonadDoubleFunc7};
Console.WriteLine("fmap f(x) = x * 0.5 over [1,0..9.0] with \" / \" operator");
var result = (listMonadDouble / ((x) => { return x * 0.5; })).Visit((x) =>
{
Console.Out.Write(x + ", ");
counter++;
if (counter % 9 == 0)
Console.WriteLine("");
});
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("App functions [x^2, x^3, x^4, x^5, [x+1, x-1]] \n" +
" over [1,0..9.0] with \" * \" operator");
var resultTwo = (listMonadDouble * listMonadFunc1).Visit((x) =>
{
Console.Out.Write(x + ", ");
counter++;
if (counter % 9 == 0)
Console.WriteLine("");
});
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
var funcMonadTupel = new Tuple<IMonad<Func<double, double, double>>,
IMonad<double>
>(listMonadFunc2,
listMonadDoubleTwo);
counter = 0;
Console.WriteLine("Combinate [1.0,..,9.0] with [1.0,..,9.0] and functions \n" +
" [x+y, x-y, x*y, x/y, x%y]");
var resultThree = (listMonadDouble * funcMonadTupel)
.Visit((x) =>
{
Console.Out.Write(x + ", ");
counter++;
if (counter % 9 == 0)
Console.WriteLine("");
if (counter % (9 * 9) == 0)
Console.WriteLine("---------------------------------------");
});
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
var funcMonadTupelTwo = new Tuple<IMonad<Func<double, double, IMonad<double>>>,
IMonad<double>>
(listMonadFunc3,
listMonadDoubleTwo);
var resultFour = (listMonadDouble * funcMonadTupelTwo)
.Visit((x) =>
{
Console.Out.Write(x + ", ");
counter++;
if (counter % 9 == 0)
Console.WriteLine("");
if (counter % (9 * 9) == 0)
Console.WriteLine("----------------------------------------");
});
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
Console.WriteLine("[1.0,..,9.0] + [1.0,..,9.0] + Just(1000.0) + Nothing \n" +
" Fmap -> App -> Com -> Com2nd -> Visit \n" +
" This will take a while!! Are you ready, then press enter :-D :");
Console.ReadLine();
var resultFive = (listMonadDouble + listMonadDoubleTwo)
.Concat(new Just<double>(1000.0))
.Concat(new Nothing<double>());
var resultSix = (ListMonad<double>)resultFive;
resultSix = resultSix / ((x) => { return x * 100.0; }) * funcMonadTupel *funcMonadTupelTwo;
resultSix.Visit((x) =>
{
Console.Out.Write(x + ", ");
counter++;
if (counter % 9 == 0)
Console.WriteLine("");
if (counter % (9 * 9) == 0)
Console.WriteLine("-------------------------------------------");
});
Console.WriteLine("\n___________________________________________________________");
Console.ReadLine();
}
Conclusion
At the beginning I had no idea where I would end. But I like what I produced.
And I got a deeper understanding about functional programming, what a type is, or how it can be seen, and much more. And I even saw what you can only do with a real function programming language like Haskell. I read an
article about the Rusty programming language from Mozilla and the tutorial. Im very interested in this language!
There are a lot of improvements that can be done. For example T Return() could be changed
to void Return(out T)
to get rid of the problem with Either and return. Btw. till now I've not tested the Either class. I got no compilation errors, so something should happen .
History
- September, 18, 2013 - First version published.
- Edit:
Com
and Com2nd
function description were wrong. The results are packed in a new IMonad
(not always a
ListMonad
) like its done by the App functions.