Although I think that a strong typed object oriented language should remain like this, and dynamic keyword in object oriented scenarios is a big mistake. Hoverer since NET 3.5 C# has the addition of functional languages called Lambdas, and I think that it's a great addition to the language as I use it constantly and with anonymous methods and delegates it's even better, but it wasn't really a true Lambda Calculus, some things like Curry function was very limited, but now we have C# 4.0 and the dynamic keyword, that's just great for functional programming.
Before I start any examples I would like to stress this out, that dynamic in object oriented scenario is bad, but when combined with functional it's a nice addition (more in scientific sense, as I don't see myself using a true lambda calculus in production environment).
In my article about anonymous methods, I introduced a function named curry, but in C# 3.5 it wasn't so effective, well it was but now we can take it to a new level.
So lets create a curry function:
/// <summary> /// the basic Curry function. /// </summary> /// <param name="num">num.</param> /// <returns>List of System Func dynamic dynamic.</returns> public static Func<dynamic, dynamic> Curry(int num) { int acc = num; return delegate(dynamic dyn) { if (dyn is Func<dynamic, dynamic>) { return dyn(acc); } acc += dyn; return acc; }; }
The interesting thing here is that we can check if 'dyn' is a delegate and the invoke it without knowing the type.
So now let's use the more dynamic version of the curry function:
Func<dynamic,dynamic> fun1 = Program.Curry(1); Func<dynamic,dynamic> fun2 = Program.Curry(2); var res = Program.Curry(3)(fun1(fun2)); //6
The result of the function is 6, but whats more interesting that now we can create a higher order functions, meaning that fun1 can invoke another delegate so can fun2 and those function results are being added.
But there's a better way to define such function, and the usage will be more intuitive, so let's do that now:
/// <summary> /// the Curry2 function that utilizes the dynamic keyword. /// </summary> /// <param name="num">num.</param> /// <returns>List of System Func dynamic dynamic.</returns> public static Func<dynamic, dynamic> Curry2(int num) { int acc = num; return delegate(dynamic dyn) { if (dyn is bool) { return acc; } acc += dyn; return new Func<dynamic, dynamic>(Curry2(acc)); }; }
Now this function will return a new delegate of itself each time it is invoked, but that way we would always end up with a delegate so to stop the sums I decided to pass a bool, so if we have a bool then we return out accumulated value.
var result = Program.Curry2(1)(2)(4)(5)(10)(false); //22 Func<dynamic, dynamic> fun3 = Program.Curry2(1)(2); var result2 = fun3(1)(5)(false); //9
Now this version is more useful, and clean.
C# has the ability to provide optional parameters, so the examples that I provided here can be updated, by using optional parameters in delegates, and the result of that would be super cool :-)
I'l show you, but let's do a new example using the dynamic, lambdas, and optional parameters. Lets do a sequence generator, for e.g the Fibonacci sequence, of course we could do a method that would act as a any generator of any desired sequence.
So let's define a simple Sequence Delegate with dynamic optional parameter:
/// <summary> /// Simple sequence generator delegate. /// </summary> /// <param name="predicate"></param> /// <returns></returns> public delegate dynamic SequenceFunction(dynamic predicate = null);
Note: dynamic may only be null to be optional.
Not it's time to define our function:
/// <summary> /// the Fib. /// </summary> /// <param name="num">num.</param> /// <returns>List of System Func dynamic.</returns> public static SequenceFunction Fib(int num = 1) { int acc = num; return delegate(dynamic go) { acc += num; return new SequenceFunction(Fibx(acc, num)); }; } /// <summary> /// the Fibx. /// </summary> /// <param name="current">current.</param> /// <param name="prev">prev.</param> /// <returns>List of System Func dynamic.</returns> private static SequenceFunction Fibx(int current, int prev) { return delegate(dynamic go) { if (go != null) { return current; } int tmp = current; current += prev; return new SequenceFunction(Fibx(current, tmp)); }; }
The fib function will stop on some non optional argument, and once we give a starting param for our sequence generator we will actually get a private function Fibx that generates the values, now this brings us to another interesting topic that I will not cover now, but do you see it? :-) we can return a private function in a delegate and the consumer can use this function.
Now let's see what are the benefit's of using such implementation.
//clasic fib start at 1. var fibResult = Program.Fib(1)()()()()()(true); //13 //now start at 2. dynamic fibSeq = Program.Fib(2)(); var fibSeqResult = fibSeq()()()(true); //16
And we have a very nice sequence generator, now think of the possibilities with more complex real world examples. Probably in my next article I will expand this topic and add more details, but for now that is all.
In conclusion the dynamic keyword made C# not more dynamic but more functional I would say, now we have a a fully functional language at our hands, and that is actually a good thing :-).
No comments:
Post a Comment