Sunday, December 23, 2007

Lambda Expressions

Definitely one of the hot new features of C# 3 is Lambda Expressions.  I agree that the name is definitely hot, and that it sounds so incredibly math cool, that I just have to use it.  But, I have no idea what it is.  Not having much of a formal math background, the only exposure I've had to Lambda anything was Revenge of the Nerds.

This is the result of the research I've done to figure out exactly they are what they are intended to be used for.

Lambda Calculus

If you have messed around with Lambda Expressions in C# already, the following mathematical definition around Lambda Calculus provides quite a bit of clarity around the expression structure and notation.

From the mathematics standpoint, Lambda Expressions are based upon Lambda Calculus, which is a formal system that puts emphasis on function definition.  In math, if you were to have the function: f(x) = x+3, it would be represented in Lambda Calculus as: λx. x + 3.  If I were then to given f(2), in Lambda Calculus it would be given as (λx. x+3)2.

Evolution of Delegates to Lambda Expressions

So how does this concept benefit us in C#?  In C# 1.0, we were given the feature of delegation, which allowed us to essentially pass functions as an argument to another function.  Below is a really simple example of C# 1.0 delegation:

public delegate string MessageDelegate(int i);
 
public static void PrintMessage(MessageDelegate func, int i)
{
    Console.WriteLine(func(i));
}
 
public static string MessageCreator(int i)
{
    return string.Format("Printing Number: {0}.", i);
}
 
static void Main(string[] args)
{
    PrintMessage(new MessageDelegate(MessageCreator), 10);
}

With C# 2.o, a feature was introduced called anonymous delegates.  With anonymous delegates, you could do the same above but by creating the delegate on the fly:

public delegate string MessageDelegate(int i);
 
public static void PrintMessage(MessageDelegate func, int i)
{
    Console.WriteLine(func(i));
}
 
static void Main(string[] args)
{
    PrintMessage(delegate(int i) { return string.Format("Printing Number: {0}.", i); }, 10);
}

Now, in C# 3.0, with Lambda Expressions, I can do essentially the same thing, but I get to do it a little more succinctly:

public delegate string MessageDelegate(int i);
 
public static void PrintMessage(MessageDelegate func, int i)
{
    Console.WriteLine(func(i));
}
 
static void Main(string[] args)
{
    PrintMessage(i => string.Format("Printing Number: {0}.", i), 10);
}

Take it even one step further, and I can get rid of the delegate declaration altogether with the new generic Func class.  The Func class takes two generic arguments in it's declaration, an input type and an output type:

public static void PrintMessage(Func<int, string> func, int i)
{
    Console.WriteLine(func(i));
}
 
static void Main(string[] args)
{
    PrintMessage(i => string.Format("Printing Number: {0}.", i), 10);
}

A Much Better Usage

You can see as the previous example progressed, it became a worse and worse example.  Why not just call Console.WriteLine("Printing Number: 10"); and be done with it.

Here's a much better example using Extension Methods combined with Lambda Expressions.  In this case, what I want to do is perform an operation on a collection of items.  Let's use the example of an order of items, and I want to total the number of items.  Here's a great way of doing that:

class Program
{
    static void Main(string[] args)
    {
        List<OrderItem> items = new List<OrderItem>
        {
            new OrderItem {ProductID=1, Quantity=1},
            new OrderItem {ProductID=4, Quantity=3},
            new OrderItem {ProductID=18, Quantity=2}
        };
 
        int totalQuantity = items.Total(p => p.Quantity);
 
        Console.WriteLine(totalQuantity);
        Console.ReadLine();
    }
}
 
public class OrderItem
{
    public int ProductID { get; set; }
    public int Quantity { get; set; }
}
 
public static class OrderCollectionHelper
{
    public static int Total<T>(this IEnumerable<T> coll, Func<T, int> predicate)
    {
        int total = 0;
 
        foreach (T item in coll)
        {
            total += predicate(item);
        }
 
        return total;
    }
}

You can see that there's quite a bit of potential in working with Lambda Expressions.  From here you can go even further into expression trees, creating a very rich language with very little code, which is a post (or ten) of it's own.

All of this has become really evident with LINQ, as it is a very powerful implementation of Lambda Expressions.

References

New "Orcas" Language Feature: Lambda Expressions - Scott Guthrie's Blog
Lambda calculus - Wikipedia

1 comments:

Anonymous said...

You're probably aware of this, but for the example of wanting to sum up numbers, Microsoft has already written the extension method for you in the System.Linq namespace. You can use the following code and then you don't even need the "Total" method:

int totalQuantity = items.Sum(p => p.Quantity);