January 2008 - Posts

Welcome back to this blogging series on functional programming. After some scene-setting in the previous post, let's get into the real stuff now.

 

A quick analysis

So, what's functional programming all about? It might be a little shocking, but it's all about programming with functions. The first part should be well-known to all readers of my blog, but what's a function? And by function we mean - yes, fasten your seatbelts - the mathematical concept.

A function is a special type of a relation that associates the members of one set (called the domain) uniquely with the members of another set (called the codomain or range).

I won't go in much more theoretic details (such as explaining what a relation is, the relation with set theory, etc), for full details I refer to great sources on the internet such as Wolfram Mathworld (where geeks will even find the "function of the day" webpage).

Let's make it more concrete starting from some notation:

f : D -> R, f(x) = ...

where f is the name of our function, D denotes the domain (for example, the natural numbers, say int) and R stands for the range (for example, the same set of natural numbers int). The ... part is intentional and can contain anything that "does something with some variable x from the domain, returning a single value from the range", requiring that the operations used in it are well-defined (e.g. mathematical operators on natural numbers).

So, what are the key takeaways?

  • Uniqueness: members in the domain correspond uniquely to one (and only one) member from the domain.
  • Time-invariant: the application of a function to give (constant) argument always yields the same result, regardless of the moment of application.

 

Our own little syntax

What's next? Some nice and smooth syntax that doesn't look too frightening (once we get into lambda expressions I'll introduce a more terse syntax), so let's stick with some C#-ish style:

R f(D)

stands for the signature of a function mapping arguments in the domain D onto values in the range R. Let's give a simple sample:

bool IsEven(int)

Notice I don't give the arguments a name yet, first we'll just reason about the signatures. We can go even further, by eliminating the function's name as follows:

bool(int)

Which is the signature of any function taking in an int and returning a bool (yes I know there is some .NET 3.5 class that does precisely this, relax we'll get there eventually :-)).

Notice we didn't cover functions with more than one argument yet. Strictly speaking we don't need these (I'll explain why in a moment) which might be shocking once more. More important however is the fact that a function returns one single value, so what if we want to return more than just one "thing"? Just wrap it in a more complicated structured "thing", called a tuple:

(int,int)

This tuple has two ints and could represent a point (or a complex number, or a monetary value, or ...). All of a sudden a signature like this:

(int, int) (int, int)

denotes a function taking in a tuple of two ints, returning another of these. Did I mention already we don't need multiple arguments (okay, this is a lame escape route, but we'll see a more appealing argument later on)?

Now, what about a function like the following:

bool (bool(int), int)

This function takes in a function with signature bool(int) itself, as well as an int, and returns a bool. What the function does inside remains a mystery though, so let's define the function.

 

Function definitions

The missing piece above is the definition of what a function does. From our takeaways we remembered that functions don't change their value across calls with the same arguments. Keep this in mind (the reason why this is important follows later). Now, let's define a function:

bool IsEven(int) := IsEven(a) = a % 2 == 0

where := stands for "is defined as". Notice we're repeating the function name on the right-hand side, this time replacing the "int" argument with a dummy name "a". The word dummy is very important for the discussion: in the function definition it doesn't have any meaning, it just acts as a placeholder. The reason for repeating the function name might look a little weird since we could well write:

bool IsEven(int a) := a % 2 == 0

Again, there are some good reasons for doing it the more verbose way and we'll get back to it when introducing pattern matching.

 

Function application

Given the function from the previous paragraph, we can apply the function as follows:

IsEven(5)

Applying the function is a mechanical thing, binding the value 5 to the dummy a, which is based on substitution:

IsEven(5)
= [IsEven(a)][a := 5]
= [a % 2 == 0][a := 5]
= 5 % 2 == 0
= 1 == 0
= false

which literally means we can drop the IsEven(5) occurrence and replace it by false. Why is this important? Assume you have a "program" (loosely defined) where you find two occurrences of IsEven(5), it's sufficient to do the mechanical transformation once and only once (since a function always returns the same value for the given argument, regardless of when you call it). The pure characteristic of functions allows us to apply various optimizations in the evaluation strategy, which is one of the places where functions touch the multi-core trends.

 

The formal way

What we've discussed above is lambda calculus in disguise. Lambda calculus was created by Church and Kleene around 1930. Some properties of lambda calculus are its declarative nature, the fact it's stateless (which we'll elaborate on quite a bit) and operates in an immutable way (you can trust it to give an argument, it's value won't be changed by it). Let's redefine the Even function in lambda calculus:

λa . a % 2 == 0

The part on the left of the dot denotes the function's parameter a being prefixed by the Greek letter lambda. Again, the parameter just acts as a dummy that happens to occur (although that's not really needed) somewhere in the part on the right of the dot.

Having the possibility to declare a function is one thing, manipulating it is another thing. To do this, we need a set of rules which happen to be called after other Greek letters:

  • α (alpha) conversion is used to rename dummies, that is: our function from above can be rewritten as:

    λx . x % 2 == 0

    by renaming a into x. You could see this as a lightweight refactoring mechanism. Of course, alpha conversion can only occur if there's no "name clash" (which needs discussion of free and bound variables). Alpha conversion is not just a convenience rule, in many cases it's a much required tool to eliminate name clashes when composing functions.
  • β (beta) conversion is the difficult word for function application. Function application is defined in terms of substitution, i.e.:

    (λx . f) y = f[x := y]

    The left hand side should be read as "a function f of an argument x, applied y" while the right hand side says this is the same as replacing all occurrences of dummy x in f by y. For example:

    (λx . (x % 2 == 0)) 5 = (x % 2 == 0)[x := 5] = (5 % 2 == 0) = (1 == 0) = (false)

    introducing a few additional pairs of parentheses for "clarity". As you can see, function application gets rid of one parameter at a time.
  • η (eta) conversion might be the most exotic one in this discussion. It's a means to deal with extensionality, a big word to say that two functions can be compared for equality (Leibniz). Essentially, it says that the abstraction of a function application is the function itself:

    (λx . f) x = f

    Strictly speaking this can only be done if x doesn't occur as a free variable in f.

Notice the way we write down functions and their arguments in a prefix manner; the fact lots of today's programming languages have infix operators is just a matter of convenience:

add = (λxy . x + y)

add 1 2 = 1 + 2

People who had prior exposure to LISP (just to name one language) will be familiar with the prefix-notation (and lots of parentheses of course), like:

+ 1 2

Higher order functions

Let's step back for a second. In the sample above I introduced a function add that takes two arguments as having the argument list λxy. But do we really need such a syntax? Strictly speaking we could write:

λx . λy . x + y

An important thing to move forward is to point out the associativity (give me parentheses!):

λx . (λy . x + y)

Hmm, a function with one argument x? Exactly! So, what does it return? The left hand side reveals it: another function. Let's apply it to see what happens:

(λx . (λy . x + y)) 3
= (λy . x + y)[x := 3]
= (λy . 3 + y)

So, we don't need multiple arguments if we allow a function to return a function by itself, meaning that each function "eats" one argument, possibly returning a function that takes care of eating the remainder arguments.

In our previous notation this could be written as:

int(int) (int)

Which is a function that takes in an int and returns a function that on its turn takes in an int and returns an int. So, a concrete function Sum is now a function of one argument, returning a new function that takes another argument:

int(int) Sum(int) := Sum(x)(y) = x + y

Look at the syntax on the right-hand side: a Sum with two parameters (each with their own set of parentheses).

In Haskell style of notation this would be written as int -> int -> int, or equivalently int -> (int -> int) due to right associativity. The technique of partial application is also known as currying (from Haskell Curry); the fact that a function can return a function by itself reveals the fact we should think of functions as data. In F# the above looks like this:

let add a b = a + b
let addtwo = add 2

where the first function is capable of taking a maximum of two arguments, but less arguments are welcome too, as done in the second line. So, the second function can only consume one (remaining) argument. A sample run in F# is shown below (we'll dedicate separate posts to F# later):

image

In C#, there's no such thing as partial application out-of-the-box, although you could work around it (as we'll see later).

 

Evaluation order

After this (important!) side step to higher order functions, let's come back to our lambda calculus rules: why do we care about these at all? One very good reason is to discuss the evaluation order of function applications (geeks might want to learn about the Church-Rosser theorem to get the bigger picture). Let's start by defining a few (recursive) functions, for which I'll use F# notation again:

let rec fib n = match n with
      1 -> 1
    | 2 -> 1
    | n -> fib(n - 1) + fib(n - 2)

let rec fac n = match n with
      1 -> 1
    | n -> n * fac(n - 1)

The first function calculates a number of the Fibonacci sequence (1 1 2 3 5 8 13 21 ...) and the second defines the faculty function (n! = 1 * 2 * ... * (n - 1) * n). I leave it as an exercise annex brain-teaser to define these in terms of lambda calculus (hint: anonymous or not?)...

The F# notation can be read as "let the recursive function fib with argument n be defined as a match of n with ..." where the ellipsis indicates a | separated list of matches written down as l -> r, where l is the match criterion and r is the result. You can compare this with a switch statement although it differs significantly: it's much more powerful and it's an expression (which implies it has a value) rather than a statement (yes, even if's are expressions in F#).

Now define a function like this one:

λx . x * x

which is a simple square function. Let's apply the function to an argument now:

(λx . x * x) (fac 10)

Say we do beta conversion first, also known as normal-order reduction, we'll end up with:

(fac 10) * (fac 10)

and further reduction will calculate "fac 10" (= 3628800) twice, which is definitely a waste of time. Even worse, every argument to the square function will be evaluated twice, no matter how complex it is (it could well be one page long, requiring an hour of calculation).

An other application would evaluate the (fac 10) part first, yielding:

(λx . x * x) 3628800

which is clearly more efficient. So, I hear you wondering already what's the most efficient one? It depends. The latter form we discussed is one you see quite often in familiar programming languages, where the arguments of a function call are evaluated before pushing them on the stack (call by value). However, this can be harmful as well. Consider the following:

let cond c f g = match c with
      true -> f
    | false -> g

Now, what happens if we call the function cond as follows?

cond true (fac 10) (fac 1000000)

When applying call by value evaluation, the system will be quite busy calculating (fac 1000000) only to discard the result of it when cond is reduced. This problem occurs with the Iif function in VB, while a ternary operator (?: in C# and the new use of the If keyword in VB 9.0, see an earlier post of mine on the subject) avoids the problem altogether, by applying call by need reduction (a form of lazy evaluation, Haskell is the most proficient user of this strategy). By the way, in languages that allow side-effects, call by need can be considered harmful by itself since the side-effects are now no longer predictable (as far as predictability goes in a world of side-effects of course) just by looking at an expression (i.e. when seeing a function call, you know arguments needs to be evaluated prior to making the call itself).

For geeks, the cond function defined above can be rewritten nicely in terms of lambda calculus using Church Booleans (a similar concept as Church numerals):

true := λab . a
false := λab . b

(yes, true and false are functions too!) which can be used as:

cond := λcfg . c f g

applied in an example:

cond true (fac 10) (fac 1000000)
= (λcfg . c f g) (λab . a) (fac 10) (fac 1000000)
= (λfg . (λab . a) f g) (fac 10) (fac 1000000)
= (λfg . f) (fac 10) (fac 1000000)
= (fac 10)

clearly, this evaluation is more efficient than blind applicative order reduction, which would

cond true (fac 10) (fac 1000000)
= (λcfg . c f g) (λab . a) (fac 10) (fac 1000000)
= (λfg . (λab . a) f g) (fac 10) (fac 1000000)
= (λfg . f) (fac 10) (fac 1000000)
= (fac 10)

Another evaluation methodology is call by name which comes close to call by need. In this strategy, the arguments to a function are captured in little thunks and passed on to the function which can evaluate it when required (by calling through the thunk). Algol uses this strategy. The main difference with call by need is that results of invoked functions aren't kept around for subsequent calls (e.g. calling fac 10 twice, maybe in two completely different places throughout an execution flow, could be optimized by keeping the calculated value in some sort of cache, given that the function fac is pure), a technique called memoization.

 

Purity

The word you've been waiting for... The whole discussion above boils down to the question whether or not we consider functions to be "pure" (meaning all the lambda calculus rules apply in a strict way). If there's no notion about global state, what's a program supposed to look like? A bunch of calculations that can be reduced to a constant? All good questions, but let's keep the answers for later. First of all, I want to convince the reader about the useful constructs offered by this functional style.

 

The C# way

Enough theory for now, let's make it somewhat more concrete using real C# style of notation. First, let's introduce a function in terms of a generic delegate:

delegate R Func<R>();
delegate R Func<T1, R>(T1 t1);
delegate R Func<T1, T2, R>(T1 t1, T2 t2);
delegate R Func<T1, T2, T3, R>(T1 t1, T2 t2, T3 t3);
...

which is what we have in the System namespace from .NET 3.5 on. Since we don't have "params" functionality on generic parameter lists, the number of overloads is limited (which means there's an upper bound on the number of parameters) but feel free to define additional ones yourself if you need more parameters (but keep in mind you don't need additional parameters per se, as illustrated two paragraphs ago). We'll stick (for now) with a limited view on the world:

delegate R Func<T1, R>(T1 t1);

So, how would you define IsEven? Let's start by creating a method:

bool IsEven(int x) { return x % 2 == 0; }

which can be referred to by means of the delegate as follows:

Func<int, bool> f = IsEven;

allowing for function application like this:

bool is5Even = f(5);

Now, if we don't want to go through the burden of defining a helper method to achieve this result, we could use a lambda expression (new in C# 3.0) instead:

Func<int, bool> f = x => x % 2 == 0;

which is essentially the same as (C# 2.0):

Func<int, bool> f = delegate (int x) { return x % 2 == 0; };

The mechanical translation to a theoretic lambda expression (starting from the C# 3.0 syntax) is trivial.

What about our higher-order Add function defined earlier? Remember the working of it, consuming only one argument, returning another function to carry out the rest of the calculation. Here's a way to write it in C# 3.0:

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

That is, you can call it like:

Func<int,int> addTwo = Add(2);
int four = addTwo(2);
int five = addTwo(3);

Essentially, we've lifted the function to become a higher order one by breaking it in pieces, which gives us the power to pass around partial functions:

IEnumerable<int> GetRange(int from, int to, Func<int,int> transformer)
{
   for (int i = from; i <= to; i++)
      yield return transformer(i);
}

...

GetRange(1, 10, Add(2))

will produce the numbers 3 to 13, or we could write (using a lambda):

GetRange(1, 10, a => a + 3);

or

GetRange(1, 10, a => a * 2);

etc.

In the above we've already stepped back from a pure functional world and allowed for imperative constructs (loops, iterators) to sneak in. Diehards can think of other mechanisms to eliminate some of these (tip: recursion).

 

What's next?

In the next post, we'll play a bit more with the C# style of function notation and see how functions compose nicely. In a later post, F# will make its appearance.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Introduction

It's a long time ago I've been posting on my blog (there are certainly good reasons for it) but finally I'm back. Guess the Holiday season has had some positive influence :-). So, what's up? In this series I want to talk a little about functional programming, more specifically what it is, why you should care and how it's been lurking around the corner for a long time. Although this series covers quite some theory, you should find it amazingly simple to absorb and apply (already). For now, we won't dive into "exotic languages" (the exotic denominator depends whose opinion you ask) such as Haskell or F#; rather we'll stick with our good old friend C#. Of course, if there's a large demand for F# stuff, I'll address that as well :-).

 

Functions - why should I care?

There are lots of good reasons. Maybe you love maths (I do), who knows? But you shouldn't necessarily be a math freak to apply functional programming in real-world scenarios in practice. A very important trend in computing nowadays is the introduction (well, we're already way beyond that, maybe omnipresence is a better word for 2008) of multi- and many-core computing (not to talk about NUMA and the steep climb of virtualization yet, which puts things in yet other dimensions). The fact I'm writing this post on a 1,5 year old laptop with a sticker "Centrino Duo" illustrates this. As I started to write this post I left Task Manager running in the background and here's the result:

image

Not much going on in Multi-Core Wonderland isn't it? Okay, I didn't put much stress on my machine but guess what, here's a similar screenshot taken while running some "busy" program:

image

However, only one half on my investment is working actively on the job: processor (or better: core) number two seems to be damn busy while the other one is wasting it's idle time doing nothing really useful. So, why would I buy a multi-core machine anyway if software isn't taking advantage of it? Why is the following application only wasting time from one core?

class Busy { static void Main() { while(true); } }

In this case it's a good thing obviously (no-one wants a busy loop to bring down the entire system), but what if that inner loop was doing something really useful? Some programs out there today (just to mention a few of the big rocks: SQL Server, MSBuild 3.5 with /m[axcpucount] flag) take advantage of the available computing power but the inner workings of those beasts are (incredibly) complex which makes testing, debugging, etc so much harder than it should be. By the way, isn't it funny (or remarkable) that the samples I mentioned are not really "desktop software", but rather machinery that's typically used in environments where lots of computing power is available (database servers, build machines, developer workstations)? What about all other sorts of applications? Agreed, we have wonderful APIs to make things "easier", and yet more high level concepts (I think of the BackgroundWorker) but let's step back from this for a while...

So, how to write software that can leverage the power of all cores while keeping developer concerns relatively low (read: not having to worry about locks, semaphores, race conditions, deadlocks and other cumbersome things)? Believe it or not, functional programming can help us to answer this question - and yes, it's available to all of you today :-). Other than this, functional programming can help to solve many other questions, for example how to reason about programs in a more formal (and mathematically sound) way. If you think I've taken on a marketing role, don't worry - we'll get into technical details in the next parts of this series. For now, I just want to set the scene in a more philosophical manner.

 

Partitioning the world

If functions represent some kind of heaven on earth (admitted, I like to exaggerate quite often), why didn't we really hear about it so much earlier? Well, the answer is that the theory behind functional programming has been there for ages (long before computers as we know them today appeared on the globe's surface). Let's give (part of) the theory a name: lambda calculus.

So, given the theory existed at the time computers were invented, why wasn't this promising paradigm chosen to pave the road ahead? Simply (or oversimplified?) because of the Hhugse (functional joke) gap between this more theorist friendly world on steroids (or should I say monoids?) and the machine architectures of these early computers (architectural pieces that live through all the way to the world of x86 and x64 by the way, feel free to go back to real basics such as the von Neumann architecture). If you were asked to invent a higher-level programming language given a book on assembler - what route would you go? I guess you'd like to control the machine as tightly as you can, yet making some typical constructs (such as loops and conditionals - something like recursion simply wouldn't be on the radar yet) easier to do, hence abstracting away (= higher-level language) from the bare metal. Hooray - you've just re-invented imperative programming, just by climbing one step higher than ASM.

On the other side of the universe there were (and luckily still are) these mathematicians (that no-one should be afraid of, all of them I know are friendly men or women) who'd rather like to descent the stairs from the functional cloud to implement those principles on computers whilst keeping their familiar safe and secure home of lambda calculus in reach. Obviously their descent to map these higher level constructs on machine-friendly instructions is way longer than the easy step the imperative language "designer" (I'd rather call someone in this early stage an "artist" drawing lines between one high-level construct onto n low-level instructions) took.

After all, it's all about mapping the What (you want the program to do) on the How (the machine should produce the right output for the given input).

 

I/O in a different perspective

Re-read the last sentence of the previous paragraph, more specifically the last part on the how. The easiest definition when explaining your job as a programmer to a four year young/old (approaching asymptotically to zero, end of philosophical note) or an eighty year old/young (approaching asymptotically to the maximum age for humans, end of philosophical note) is to handle some definition that implies you "tell a computer what it should do". However, if you step back and think of that statement you'll discover it's a big lie: you're not really telling (at least directly) what the program should do, you're telling how the machine should do it. That being said, when you have to explain your job again to the now five year old (four year olds don't remember things that well yet, exceptions set apart) or the now eighty-one year old (eighty year olds don't remember things that well anymore, exceptions set apart) you might come to the conclusion your statement is a little more true (entering fuzzy logic) that it was one year ago: things have abstracted away a little more from the plumbing level to the say-what-you-want or express yourself level (intentional italics). Think of SQL (do you tell how to execute a query?; not unless you're following the "curse of the cursor" - cursor in database-sense - or if you still live in a preDated Codd-free world), *ML type of languages (as in X*, HT*, XHT*... but also as in OCa* - anyone?) and more recently LINQ (tell it what data you want, regardless of the store or query languages behind the scenes).

So, what about another timeless (?) invariant (?) definition of programming: telling how a piece of software should transform a given input to a desired output (tip: don't define software in terms of programming - the four year old will catch the cyclic definition). Think of it as I/O in the broadest sense of the word and forget about buses, devices or peripherals. I did put the words input and output in the How-camp above, where most developers see it as a familiar construct. However, the place it really belongs is the What-camp: you simply want to state what output you expect a program to yield for a given input. And that's what functions are all about: transforming some input to one single output, however that might happen. Read the previous sentence carefully: one single output.

Now, what's a bug? Using your first definition of programming you'd likely tell the kid and your grandpa that "a bug occurs when the computer doesn't do what it should do". That's a handy definition, because you project bugs on the machine and you don't mention that you could have told the computer to do the wrong thing (strictly spoken you did!). In most cases it's somewhere in between: you might be telling it the right thing but you didn't get the plumbing right. To be more precise, you couldn't tame the side-effects: exceptions appearing out of the blue, some kind of interrupt (IRQ, event, ...) you didn't treat correctly, synchronization that isn't set up correctly, etc. One slogan that fits it all: "side-effects come and get you".

What about a better definition for a bug that fits better in the functional picture: if one observes incorrect output for a given input, it's a bug. It's up to the reader to figure out who to blame it to, but since you simply state what you want (that's what a function is all about) - at least in a pure world - it will be very hard to ignore you simply defined the wrong function or defined the function wrong. I agree, I'm making quite some ameliorations in here, but you get the big picture. The only unfortunate thing about all of this: you might loose your comfortable rescue net blaming the computer for your bugs :-).

 

Conclusion

Functional programming = love (riddle: replace the lhs, a TLA or three-letter acronym for left-hand side, with a TLA that caused a "TLA-hell" and you get an (old?) slogan of someone with a TLS or three-letter surname, who did co-invent a FLA or four-letter acronym technology - substitute all TL* occurrences). You might be unaware of it, but you've definitely seen applications of the theory already: LINQ is one of these and the Parallel Extensions (will blog about these in a separate series) are another encounter of it, and with some goodwill you could promote parts of SQL (relational algebra) to the functional domain (lambda calculus) as well (I'm not going to defend nor defeat this statement).

In the next part of this series, we'll finally see some real C# code again, introducing functional programming step-by-step.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

That's right - we've published the .NET Framework Library Source Code to the public. Scott Guthrie has the full details. Getting the sources out for the WPF team has been one of my first jobs on the team, which was an interesting experience crawling through the source tree, finding out about build system stuff, and so much more. In technical terms, you now have access to the PDB files over HTTP, as outlined by Shawn Burke over here.

Anyway, enjoy debugging through the BCL sources!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

What a joke you must think when reading this post's title. Isn't the functional paradigm all about side-effect free programming and the such? Well, it turns out you're absolutely right. So, why this post? I have to admit I had yet another crazy Sunday afternoon idea that I found worthwhile to open VS 2008 for and give it a short.

A long history short: I'm having a few mail threads currently on exception filtering in the CLR and the (lack of) language support for it (cf. Visual Basic's When keyword). Brave people would extend existing compilers to add support for such a feature, others (including me in Sunday afternoon moods) would rather put some library support together to achieve similar results (that being said, what you'll see next doesn't have the real CLR exception filter semantics, but with a bit (bunch?) of Reflection.Emit stuff one could achieve this, at runtime though).

Here's my take on how to write exception handlers in a functional style (as it turns out, the only reason to call it functional is a) it works, b) it uses lambdas and c) a couple of parentheses - exercise: find these two guys in the code below :-)).

class Program
{
    static void Main()
    {
        Action body = delegate {
            Console.WriteLine("Hello");
            throw new InvalidOperationException("Bang");
        };

        body.Catch<InvalidOperationException>(ex => ex.Message == "Oops", ex => Console.WriteLine("Oops!!!"))
            .Catch<InvalidOperationException>(ex => ex.Message == "Bang", ex => Console.WriteLine("Bang!!!"))
            .Catch<Exception>(ex => Console.WriteLine("Caught " + ex.Message + " using bad style..."))();
    }
}

It's not too hard to guess how it works: Catch<T> is an extension method on the Action class, having two overloads. The first two invocations you see, have some filtering logic (ex => ex.Message == "Oops" for example), the last one omits this. The reason for overloading on the left parameter is readability: you can read each line as "catch exception when filter using this code". However, if you come from the VB camp, overloading on the right would make more sense: "Catch exception using this code When filter".

The implementation of Catch isn't that difficult either:

static class Exceptions
{
    public static Action Catch<T>(this Action body, Action<T> handler) where T : Exception
    {
        return Catch<T>(body, ex => true, handler);
    }

    public static Action Catch<T>(this Action body, Func<T, bool> filter, Action<T> handler) where T : Exception
    {
        return delegate
        {
            try
            {
                body();
            }
            catch (T ex)
            {
                if (!filter(ex))
                    throw;
                else
                    handler(ex);
            }
        };
    }
}

And it's not too hard to imagine a Finally method too:

public static Action Finally(this Action body, Action @finally)
{
    return delegate
    {
        try
        {
            body();
        }
        finally
        {
            @finally();
        }
    };
}

Going even further, what about extending IDisposable with a Using method? The using keyword in C# basically emits a try ... finally ... statement with the finally block calling Dispose on the given object. So, it becomes:

public static Action Using(this IDisposable resource, Action block)
{
    return block.Finally(delegate {
        if (resource != null)
            resource.Dispose();
    });
}

Oh, and before you ask: you don't need to worry about using variables from the outer scope thanks to closures. There are more subtle things though, which I'll let the readers find out about as a New Year's gift :-) Happy exception handling in 2008!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts