Logo




Subscribe:
RSS 2.0 | Atom 1.0
Categories:

Sign In


[Giagnocavo]Michael::Write()

# Sunday, August 12, 2007
Practical Functional C# - Part I

Edit: This first article might be a bit dense at times. However, I promise, if you stick through and read at least until Parts II and III (Loops are Evil) you will see major benefits that will totally transform your code (even C# 2.0 code!). In fact, to the first 3 people that can show me that these practices will NOT result in better code in many enterprise apps, I'll give $100 each. Post a blog comment or email me (mgg AT telefinity dot com).

Redundancy in source code is a common degeneration. But for the C# programmer, her weapons to eliminate it have been unwieldy at best. She has been able to eliminate simple, blocky, patterns, but truly writing reusable code at a fine level has not been easy.

This series will demonstrate how you can take advantage of functional programming (FP) in your work, today. The first task is to dispense with the notion that functional programming is difficult and strange. Most FP articles start with a recursive definition of the Fibonacci sequence and then talk about currying functions. Here, we're going to start off with something that every C# programmer has used many times. I can guarantee that after you absorb this series of articles, you'll be writing more concise, less buggy, more powerful software. I've seen many C# modules get cut down drastically in size. I've seen new C# code written that does multithreading and it was written correctly on the first go. I want you to see these things too.

Refactoring out common blocks of code is familiar. Probably every developer has written a helper method to initialize a database command or prepare a commonly used object. Unfortunately, refactoring only tends to happen to contiguous blocks. We do not see refactoring of complex patterns that do not fit into neat blocks. Consider the following visualizations of a program. The red blocks are the unique parts of the program, and the grey ones represent common statements.


                                             becomes

It is easy to refactor the first sequence: move the common parts into another function and call it. However, the second pattern poses quite a problem. The individual "common" blocks are too simple to move into another method. The call to the refactor method would be the same as the method itself! How can we refactor it correctly?

Well, C# itself contains keywords that refactor some of these patterns. Consider the using keyword. The following two methods are nearly equivalent:

void usingPatternExample()
{
    string result;
    StreamReader sr = new StreamReader(filename);
    try {
        result = sr.ReadToEnd();
    }
    finally {
        if (sr != null) sr.Dispose();
    }
}

void
usingKeywordExample()
{
    string result; 
    using (StreamReader sr = new StreamReader(filename)) {
        result = sr.ReadToEnd();
    }
}

I think it is obvious why everyone uses the using keyword over writing out this long init-try-something-finally-dispose pattern repeatedly. When we read code that uses the using keyword, the intent is instantly clear. We don't need to go validate the pattern to make sure it does what we think it does, we can just see "using" and know that it is correct. As an extra benefit, we can declare our disposable variable inside the using scope, so that we don't accidentally use it after it is disposed.

This is all good and dandy, until we realize that we are stuck with the handful of patterns defined as C# keywords! If I wanted to declare the variable result as an output of the using statement, I'm out of luck. If I want to initialize multiple objects in one using block, then that is just too bad. Part of the reason for this is that it was extremely ugly to do this in C# before version 3.0. Consider this C# 1.0 example to recreate the using keyword as a user-defined function:

delegate void UsingAction(IDisposable obj);
void Using(IDisposable obj, UsingAction action)
{
    try { action(obj); }
    finally { if (obj != null) obj.Dispose(); }
}

So far, so good – nothing ugly yet. The usage of this user-defined Using function on the other hand:

string result;
void UsingExample1()
{
    Using(new StreamReader(filename), new UsingAction(readIntoResult));
}
void readIntoResult(IDisposable obj)
{
    StreamReader sr = (StreamReader)obj; 
    result = sr.ReadToEnd();
}

Atrocious! The pattern of the code is shattered and complicated to follow. The main part of the code (getting the result) is forced to sit in a separate method many lines away. We're required to use fields to pass data in or out. And just to top it off, lack of generics forces a cast – we had to write StreamReader three times. The utter syntax hides our intention. In short, it is completely unusable.

C# 2.0 makes some progress. First, we can change UsingAction to Action<T> and allow the caller to specify the type. All that is needed is a constraint on the type to IDisposable. The new declaration of Using looks like this:

void Using<T>(T obj, Action<T> action) where T : IDisposable

This says that we need something of type "T", and the only restriction is that T must be a type that implements IDisposable. Then, we want an Action that works on that same type T, whatever it is. By using T instead of a specific type, we are essentially saying "we don't care about what type your object is; our function works with all sorts of types". This is the source of the name "generics". Our code is more generic; it's not tied to a specific type. Generics are an extremely important tool for refactoring patterns because patterns occur across different types.

To clean up the calling code, C# 2.0 has a feature called anonymous methods. They are exactly as they sound: methods without names. They can be declared inline, right along with the rest of the code. Additionally, they "capture" local variables, making it unnecessary to declare fields to pass data in and out.

void UsingExample2()
{
    string result; 
    Using(new StreamReader(filename), delegate(StreamReader sr) {
        result = sr.ReadToEnd();
    });
}

What a major improvement! C# 2 can infer the type of T for Using, avoiding having to write Using<StreamReader>. Apart from having to type StreamReader twice, this code is starting to approach the level of clarity that the using keyword provides.

C# 3.0 introduces lambdas. Now, that's usually a scary word from functional programming, but it's actually very simple. For C#, it's just an easier way of writing anonymous methods. Instead of having to write "delegate(ParamType paramName) { }", we can just do "paramName =>". That's all there is to it! Nothing scary at all. For example, if I want to write a lambda to add two numbers, I’d write it like this:

Func<int, int, int> add = (int a, int b) => a + b;

The part Func<int, int, int> says we have a function that takes two ints and returns an int. Next, we declare our two integer parameters: (int a, int b). Then, we define the lambda body by using the => operator. Our body consists only of “a + b”. With lambdas, if we only have one statement, we don’t need to explicitly use braces or the return keyword. Alternatively, we could have written it as:  

Func<int, int, int> add = (int a, int b) => { return a + b; };

Behind the scenes, the compiler goes and creates a method and creates the Func delegate on top of it. So, armed with this new syntax and power, let’s take another look at our Using method:

void UsingExample3()
{
  string result;
  Using(new StreamReader(file), sr =>
    result = sr.ReadToEnd()
  );
}

void usingKeywordExample()
{
  string result;
  using(var sr = new StreamReader(file)) {
    result = sr.ReadToEnd();
  }
}

This shows there is nothing that special about the built-in keywords. It is also the first step in demonstrating that some parts of functional programming are not very foreign to the C# programmer. The next articles in this series will provide some real-world application of these ideas and show how you can reduce the amount of code you have to write (and reduce the number of bugs at the same time!).

Code
Sunday, August 12, 2007 11:21:32 PM UTC  #    Comments [5]  |  Trackback

Monday, August 13, 2007 6:20:56 AM UTC
Nice!

When I first read about the changes in C# 3.0 I got very excited, but now that mono is very close to shipping them, and people start to show how they fit in real-world code, it's just great, I can't wait.

Posts like this are an important part of the future since they bring interesting stuff into the attention of developers who otherwise wouldn't pay much attention to features like this one.

Thanks.
Leonardo
Monday, August 13, 2007 10:03:45 AM UTC
Thanks Michael.
Monday, August 13, 2007 8:42:01 PM UTC
Awesome article. I've been using C# for about a year now and never new what functional programming was all about. It turns out I've been using things close to your C# 2.0 example all along. Can't wait for mono to get the C# 3.0 features.
Monday, September 10, 2007 9:55:54 PM UTC
adding 2 ints should return a long. :)
Vince
Friday, May 16, 2008 8:02:38 PM UTC
Wow, great post!

Your method of abstracting out the Using statement made me start thinking about how there are other idioms/patterns that might be good candidates for this type of thing.

The first thing that I thought of (since at the moment I'm working on a small WinForms application) was trying to do responsive (multi-threaded) user interfaces. I wrote a post that essentially takes this way of thinking and abstracts out the behind-the-scenes code that uses a BackgroundWorker, letting you focus on 3 things -- the input argument to the long-running thread, the code that needs to run on a different thread, and the code that needs to run on the UI thread when the background thread completes.

http://thevalerios.net/matt/2008/05/12/a-type-safe-backgroundworker-wrapper/
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview