Sunday, January 06, 2008 7:37 PM
bart
Exception Handling in functional style
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
Filed under: C# 3.0, Crazy Sundays