C# Functional style - Part 3
07/28/2018
2 minutes
In this post I will take the existing methods to a higher abstraction by handling them as pure C# Func.
First we need to introduce a couple of extension methods, which will help to Curry and Partially apply some of the arguments on the existing methods. In C# this is a slightly more difficult, because we would need to create overrides for each different signature based on the arity of the method.
Creating an extension method for Currying:
public static Func<TIn, Func<TOut>> C<TIn, TOut>(this Func<TIn, TOut> f) => x => () => f(x); public static Func<TIn1, Func<TIn2, TOut>> C<TIn1, TIn2, TOut>(this Func<TIn1, TIn2, TOut> f) => x => y => f(x, y); // Further overloads
Given a method this will return a function that takes a parameter and returns a function which is the input method invoked with the parameter. So to add one and two we could do the following:
((Func<int, int, int>)Add).C()(1)(2);
At this point we can curry the Add(...) and Log(…) methods, and compose these methods together:
var log = ((Func<string, int, int>)Log).C(); var add1 = ((Func<int, int, int>)Add).C()(1); var composed = log("Message In").Compose(add1).Compose(log("Message Out")); input.Select(composed).ToList();
In this case add1 is one of the examples where we curry, and straight away partially apply one of the parameters. To simplify the definition of this we can create some helper extension methods to partially apply arguments:
public static Func<TIn2, TOut> Pa<TIn1, TIn2, TOut>(this Func<TIn1, TIn2, TOut> f, TIn1 param) => f.C()(param); public static Func<TOut> Pa<TIn, TOut>(this Func<TIn, TOut> f, TIn param) => f.C()(param);
This allows to further simplify this code above by having less parenthesis:
var logIn = ((Func<string, int, int>)Log).Pa("Message In"); var logOut = ((Func<string, int, int>)Log).Pa("Message Out"); var add1 = ((Func<int, int, int>)Add).Pa(1); var composed = logIn.Compose(add1).Compose(logOut); input.Select(composed).ToList();
If we don't want to compose these methods, but rather pipe them together, we could go ahead and use the Select extension method:
input.Select(logIn).Select(add1).Select(logOut).ToList();