Functional Programming in C#

Learn how to write better C# code

Higher-Order functions

Higher order function is a function that takes one or more functions as arguments, or returns a function, or both. We are used to passing as function parameters simple objects like integers or strings, or more complex objects like collections and custom types. But C# also has good support for HOFs. This is done using delegates and lambda expressions.

A delegate is a type that represent a reference to method with particular parameters and return type. They are used to pass methods as arguments to other methods (HOFs). An example for delegate is:

public delegate int Calculate(int x, int y);

You can use lambda expression to create delegates or expression tree types. They can be passed as arguments or returned as the value of function calls. A common place to use lambdas is with LINQ (if you have used LINQ, you have used HOFs too). For example:

var numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var even = numbers.Where(x => x % 2 == 0);
// => [2, 4, 6, 8]
Func<int, bool> isOdd = x => x % 2 != 0; // assign lambda expression to Func
var odd = numbers.Where(isOdd);
// => [1, 3, 5, 7, 9]

Where is a good example of higher-order function. Its declaration is:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

The predicate parameter is a function of type Func<TSource, bool>. This allows for the caller to pass in the functionality that determines which items should be included in the result and which one should be excluded. In C# functions can return other functions. In the example bellow isDivisible takes integer and returns Func<int, bool>, which in turn is passed as parameter to Where.

Func<int, bool> isDivisible(int n)
{
return x => x % n == 0;
}

var numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var num3 = numbers.Where(isDivisible(3));
// => [3, 6, 9]

Using HOF to change the "interface" of a function

It is possible to have HOF that does not apply the function, but just return a new function. For example, if we have function that divides integer numbers

Func<int, int, int> divide = (x, y) => x / y;
divide(6, 2); // => 3

and we want to change the order of the arguments, we can define a generic HOF that swaps the arguments:

public static Func<T2, T1, R> Swap<T1, T2, R>(this Func<T1, T2, R> f)
=> (t2, t1) => f(t1, t2);
var divideBy = divide.Swap();
divideBy(2, 6); // => 3

By using this sort of HOF we can change the interface of a function to better suit our needs.

Continue reading: Functors and Monads

Resources: