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:
Ken FellowsPosted Jul 10, 2012, 4:04 PM
Hi, How does one get the output from the code in, Part III Wrap-Up? -Ken-
WilliamPosted Jan 27, 2008, 4:28 AM
Thanks for the excellent introduction! -william-