Introduction to Functional Programming in C#

Part I. Coding with (Functional) Style.

In order to understand functional coding, let's take a look at the difference between a "non-functional" for loop and a functional approach to achieve the same results.  With our first sample, we have a "non-functional" approach with a basic "for" loop that produces the numbers [0,10] and display them to the console. 

Beginning Code Sample:

for (Int32 i = 0; i < 10; i++)
    Console.WriteLine(i);

Ok, let's get started looking at the different ways to implement this in a functional manner using the 2.0 and 3.5 frameworks. To accomplish the same thing with a functional coding style, we'll create a helper method (ForEach<T>) in order to perform an action on each item in a collection of objects (IEnumerable<T>).  Also, for those of us still using 2.0, we'll first have to create a utility function to return a range of items (Enumerable.Range())

Functional Sample (.NET 2.0) - Utility methods:

public static class FunctionalExtensions
{
    public static void ForEach<T>(IEnumerable<T> items, Action<T> pPerfomAction)
    {
        foreach (T item in items)
            pPerfomAction(item);
    }
}

public
static class Enumerable
{
    public IEnumerable<Int32> Range(Int32 from, Int32 to)
    {
        for (int i = from; i < to; i++)
            yield return i;
    }
}

Implementing our code in a functional way we come up with the code (below) which looks fine as we are now achieving the same results just with method calls by passing a delegate to the Display() method in the second parameter of the ForEach() method.  Passing delegates around as parameters will be a common practice we'll be using as we are programming with functions.  The compiler knows we need a delegate in the second parameter (of type Action<Int32>) and automatically builds it for us so we don't have to explicitly declare it. 

Functional Sample (2.0) - Implementation

class Program
{
    static void
Main(string[] args)
    {
        FunctionalExtensions.ForEach(Enumerable.Range(0, 10), Display);
    }

    public void Display(Int32 i)
    {
        Console.WriteLine(i);
    }
}

So you are probably wondering at this point "what's the up-side?". Well, there are a couple of cool things that happen as a result of having a functional programming style.  These include:

(1) Code Reuse: We can now reuse our utility functions which will result in a much smaller code base to achieve the same results.  This is not apparent with our simple "for" loop demonstration, but wherever we have repeated code in our project, we can factor it out into a reusable function and avoid writing the same utility methods over and over and avoid having to maintain repeated functional code in multiple places. 

(2) Ease of Testing: Debugging and testing is easier because each function is not at the mercy of the state of some external object that we have to worry about replicating with our test.  If we are strict about sticking to a functional programming model each function should not rely on any object state outside the scope of the function...  in other words the function will only deal with the parameters passed in as arguments.  If we stick to this simple rule and there is a problem with a function it will be easy to recognize and write a unit test to enforce our fix it because each function will be independent of any context or object state.

(3) Concurrency: If we stick strictly to a functional programming style and make sure there are no side-effects, our code is thread-safe by default because each thread will only be accessing objects instantiated within it's own thread stack and so there will be no objects having two threads trying to access them at the same time.  In an upcoming article I'll take a look at how we can easily execute a functionally written program both synchronously and asynchronously (it's surprisingly easy). 

The big down-side I see in using a functional approach is the impact on the number of objects in the heap (memory) when we are having to instantiate many more objects (one for each function call) instead of sharing a common object.  We should always keep this trade-off in mind when building high throughput applications.  However, if you are like me and live by the motto "make it work, then make it work better" this may never become an issue and if it does it can usually be mitigated through some careful refactorings. 

Let's walk through the steps to get us to the nice-looking and easy to read syntax using lambda expressions available with the 3.5 framework (which are  just a prettier way of writing an anonymous method). But first, having a bunch of domain-specific utility methods sprinkled around can be really confusing and hard to maintain,  so using anonymous delegates (introduced in the 2.0 framework) we can consolidate our code in one place.  It's not as pretty as the first implementation, but this will get much better when we get to the lambda expressions later in the article.

Functional Sample (2.0) - Anonymous Method Implementation

FunctionalExtensions.ForEach(
    Enumerable.Range(0, 10),
    delegate(Int32 i) { Console.WriteLine(i); });

Before we get to the pretty lambda expression, we will use a language feature in the 3.5 framework that will help us clean up our code a bit. 

First, in the 3.5 release we get the Enumerable.Range() method as a part of the "System.Linq" namespace so we can get rid of this extra method.

Second, we are able to define extension methods and if we change our original ForEach<T>(IEnumerable<T> items, Action<T> pPerfomAction) method signature by adding the "this" keyword on the first parameter, we are able to call our method in a much more readable way.

Functional Sample (.NET 3.5) - Utility Method

public static class FunctionalExtensions
{
    public static void ForEach<T>(this IEnumerable<T> items, Action<T> pPerfomAction)
    {
        foreach (T item in items)
            pPerfomAction(item);
    }
}

Functional Sample (.NET 3.5) - Implementation Using Extension Method

Enumerable.Range(0, 10).ForEach(delegate(Int32 i) { Console.WriteLine(i); });

So now we are down to one utility method and a syntax that is much more human-readable.  If we examine what is being done from left to right, the program is easy to understand (at least until we get to the anonymous delegate).

Finally, we can now use a lambda expression in place of the anonymous delegate and we are back to a very readable syntax that performs the same actions as our traditional "for()" loop.

Functional Sample (.NET 3.5) - Lambda Implementation

Enumerable.Range(0, 10).ForEach(i => Console.WriteLine(i));

Personally, I usually find it is a bit more readable to put each method call on a separate line.

Enumerable
    .Range(0, 10)
    .ForEach(i => Console.WriteLine(i));

So now we have a completely functional implementation of our "for()" loop.  Once you understand the syntax, it is not any more difficult to understand that the original implementation.  Of course, this is just scratching the surface of what we can do.  Let's look at a more complex sample showing how we would  read data from a file in a purely functional style.

Part II. FileRead

Let's look at the two styles to write code that will both read a text file and count the number of occurrences of each word.  To make things simple, we'll define a words as anything separated by a blank space. 

First, we have our code in a "non-functional" programming approach.  With formatting in place to make the code readable we end up with 18 lines of code:

private static void NonFunctionalExample(string[] args)
{
    using (StreamReader rdr = File.OpenText(args[0]))
    {
        String contents = rdr.ReadToEnd();
        String[] words = contents.Split(' ');

        for(Int32 i=0; i< words.Length; i++)
            words[i] = words[i].Trim();

        Dictionary<String, Int32> d = new Dictionary<string, int>();

        foreach (String word in words)
            if (d.ContainsKey(word))
                d[word]++;
            else d.Add(word, 1);

        foreach (KeyValuePair<String, Int32> kvp in d)
            Console.WriteLine(String.Format("{0} count: {1}", kvp.Key, kvp.Value.ToString()));
    }
}

I'll show you the functional-style code (below) and then we'll look at the implementation details. With formatting to make the code more readable, our new version has 9 lines of code (not counting the reusable helper methods) that accomplish the same thing we did with 18 lines of code in the previous sample.  That's a significant reduction especially considering each line is much shorter in the functional example.

private static void FunctionalExample(string[] args)
{
   File.OpenText(args[0]).Use(stream =>
       {
            stream
                .ReadToEnd()
                .Split(' ')
                .Convert(str => str.Trim())
                .GetCounts((x, y) => x == y)
                .ForEach(kvp => String.Format("{0} count: {1}", kvp.Key, kvp.Value.ToString()));
        });
}

Although there are many differences of opinion on the subject of readability, I think that the functional approach is much easier to understand at a glance and more "human-readable" than the non-functional approach.  The tricky thing is just understanding how the functions chain together. As you can see, there is now much less code in the method body itself.  We will be pulling a lot of the reusable methods out into a utility class which would actually be put in a separate assembly for reuse. 

A. Converting "using" blocks to functions.

In order to execute a "using" block from a function, we need to build the following utility method:

public static void Use<T>(this T item, Action<T> action) where T:IDisposable
{
    using (item)
    {
        action(item);
    }
}

Now we have a very reusable utility method that allows us to maintain an IDisposable object only for the length of the function call and we don't ever have a reference to the object outside the using block.

Generally is it bad practice to have a using block use an object declared outside the block.  This is because the object could be accidentally accessed after Dispose() is called when the end of the using block is reached.  This will cause unexpected results because there is a reference to a disposed object from the variable.

// dangerous using block
Disposable
d = new Disposable();

using
(d)
{
    // do something with d;
}

// code could be misleading because
// the "d" variable is still available here and
// could accidentally be accessed

This is a safer way to use the "using" block because the scope of the object is only within the code block where it should be referenced.

using (Disposable d = new Disposable())
{
    // do something with d;
}

With the functional approach we ensure that the implementation is safe because we don't keep a reference to the object in a variable that could exist anywhere but inside the "using" block.  The syntax is a bit more condensed as well.

// functional call using our utility method
new
Disposable().Use(d =>
{
    // do something with d;
});

 B. Conversion (partial function definition)

Partial function definition is a way to build up a function piece-by-piece.  From an OOP perspective, we could think of this process as a wrapper used to transform one "type" to another (which is why I named this simple wrapping method "Convert()".   Using the "yield" keyword, we have a very simple construct for transforming input which is easy to understand and easy unit test and can be reused in many other projects if we separate it into a seperate utility assembly.

public static IEnumerable<U> Convert<T, U>(this IEnumerable<T> items, Func<T, U> conversion)
{
    foreach (T item in items)
        yield return conversion(item);
}

If you look at our final code sample, we are using the convert method to trim the empty space from the ends of the strings by passing a method defined using lambda syntax:

private static void FunctionalExample(string[] args)
{

   File.OpenText(args[0]).Use(stream =>
       {
            Stream
                .ReadToEnd()
                .Split(' ')
                .Convert(str => str.Trim())
                .GetCounts((x, y) => x == y)
                .ForEach(kvp => String.Format("{0} count: {1}", kvp.Key, kvp.Value.ToString()));
        });
}

C. Counter Method.

The "GetCounts()" method is also a partial function definition.  When we look at code implementing this method from a functional perspective, we get code that works, but will not perform well because the entire list is traversed multiple times. Notice however, that we are already able to reuse our Convert() utility method.

public static IEnumerable<KeyValuePair<T, Int32>> GetCounts<T>(this IEnumerable<T> items, Func<T, T, Boolean> pEqualityComparerMethod)
{
    return items
        .Distinct()
        .Convert(item => new KeyValuePair<T, Int32>(
            item,
            items.Count(i => pComparerMethod(i, item)))
        );
}

Instead of the sample above, we can have much more efficient code by using one traversal of the group.  Fortunately, because C# has a foot in both worlds, we can choose the "best" way to implement our methods given any situation.  We just need to be careful to not do anything that would have side effects when we are using a non-functional style so that we retain the advantages of the functional approach like concurrency, reuse, and ease of testing and debugging. 

public static IEnumerable<KeyValuePair<T, Int32>> GetCounts<T>(this IEnumerable<T> items, Func<T, T, Boolean> pEqualityComparerMethod)
{
    Dictionary<T, Int32> result = new Dictionary<T, int>(new EqualityComparer<T>(pEqualityComparerMethod));

    foreach (T item in items)
        if (result.ContainsKey(item))
            result[item]++;
        else result.Add(item, 1);

    return result;
}

In order to specify how the items should be compared,  I created a EqualityComparer<> object to pass to the dictionary that allows us to use the IEqualityComparer<T> interface in terms of it's primary function.  Hopefully, in future versions of the framework the Dictionary<TKey, TValue> object (and other collections) will have an additional constructor that takes Func<Bool, TKey, TKey> in addition to the IEqualityComparer<TKey> interface, but for now we will have to use this utility class.

    public class EqualityComparer<T> : IEqualityComparer<T>
    {
        public EqualityComparer(Func<T, T, Boolean> pEqualityComparerMethod)
        {
            EqualityComparerMethod = pEqualityComparerMethod;
        }

        public Func<T, T, Boolean> EqualityComparerMethod { get; set; }

        #region
IEqualityComparer<T> Members

        public bool Equals(T x, T y)
        {
            return EqualityComparerMethod(x, y);
        }

        public int GetHashCode(T obj)
        {
            return obj.GetHashCode();
        }

        #endregion

    }

Part III Wrap-Up

There is much (much) more to functional programming, but hopefully this article provided a good start towards understanding how we can program in C# using a functional style and you can see some of the benefits of using this style.  The best way to lean this approach is to exercise by taking some code and try implementing it both ways or try to refactor some simple code into function calls.

I'll leave you with this final thought.  An additional benefit to having functionally written code is that is it easy to refactor. It is extremely easy to modify functional code by injecting further processing into a function chain.  For example, let's say we wanted to order the output of our code before we display it to the console.  Doing this using a non-functional style would be very cumbersome in comparison to the code below.

private static void FunctionalExample(string[] args)
{
    File.OpenText(args[0]).Use(stream =>
        {
            stream
                .ReadToEnd()
                .Split(' ')
                .Convert(str => str.Trim())
                .GetCounts((x, y) => x == y)
                .OrderBy(f => f.Key) // this is easy to sort -- try adding this functionality on the non-functional sample!
                .ForEach(kvp => String.Format("{0} count: {1}", kvp.Key, kvp.Value.ToString()));
        });
}

Until next time,
Happy coding


Similar Articles