So I have to admit I've been late in jumping to the "code as data" magic! I did play around with LINQ and the lambda syntax back last year with the May CTP, and found it really simple and powerful - reminding me of the days I worked with ML. I did not, however, play with System.Expressions until recently.
Lambda expressions can be compiled as expression trees. Expression trees are data objects that efficiently represent the algorithm of evaluating the lambda expression in memory. These expression trees can be manipulated by code – the concept of metaprogramming. Also, by calling Compile on the resulting expression tree, you can dynamically generate executable code corresponding to the expression and then you can call Invoke to actually invoke the compiled code with the appropriate parameters. Calling Compile returns a delegate of type T which is the IL dynamically generated from the expression tree. Converting data (expression tree) to code whenever evaluation is required is pretty powerful!
The compiler is smart because if you assign a lambda expression to a delegate type like Func<..>, then it generates IL, but if you assign the lambda expression to Expression<T>, the compiler doesn’t generate IL – it generates the in-memory tree of objects that represent the structure of the expression.
A classic use of this is to write a parser which takes a string that represents an expression and converts it into an expression tree representing the code needed to evaluate that expression. Once the tree is constructed, you have an expression evaluator by just calling Compile. Neat.