Welcome to MSDN Blogs Sign in | Join | Help

In the earlier posts on dynamic operations I talked about dynamic binders and rules. Then, rule was two things combined - test and target, both represented as expression trees. Test would determine whether arguments of the dynamic operations 'fit the target' and target would then perform the operation.

These rules, I mentioned, were cached inside of DLR for later reuse (so that DLR didn't have to ask the language binder over and over for the same rule). Today we will explore these caches and also changes that happened in the DLR since those posts. I will base this post off of the latest published sources on CodePlex, and in few places I will point out what it is that you have to look forward to in the near future as we publish next version of the sources. I am trying to find the way to publish our sources more frequently.

As the language binder produces the rule for performing a given dynamic operations, DLR will add it to the caches. DLR maintains 3 levels of caches so let's dive in and see what and where they are.

Level 0

When the dynamic operation is happening, DLR calls into the dynamic site which will in turn immediately call a delegate stored on the dynamic site. The delegate is a level 0 cache. if we were to disassemble the delegate, we would see the cache lookup expressed directly as code below.

// Level 0 cache lookup

public static object _stub_(Closure c, CallSite site,
    CodeContext context, object obj1, object obj2) {

    // Trying to match the cache entry for strings

    if ((obj1 is string) && (obj2 is string)) {

        // Cache hit on strings

        string str = ((string)obj1) + ((string)obj2);
        return str;
    }

 

    // Trying to match the cache entry for doubles

    if (((obj1 != null) && (obj1.GetType() == typeof(double))) &&
        ((obj2 != null) && (obj2.GetType() == typeof(double)))) {

        // Cache hit on doubles

        double num = ((double)obj1) + ((double)obj2);
        return num;
    }

 

    // Level 0 cache miss, call for help

    return ((CallSite<DynamicSiteTarget<object, object, object>>)site).Update(
        site, context, obj1, obj2
    );
}

One idea that will apply to all of the cache levels is most visible here ... the cache lookup happens by executing the code, or in other words, by executing the rules in the level 0 cache.

Each dynamic site has its own Level 0 cache and the idea here is that well behaved programs generally only work with small set of types at a given call site and for such programs the level 0 cache will rarely experience a cache miss.

Level 1

However, a level 0 cache miss can happen. And when it does, the delegate cries for help by calling back into DLR. If you study the code above carefully and compare it to some of the older examples, you notice that the "cry for help" code has changed a little. Not to worry, it still serves the same purpose.

In general, the ultimate receiver of the 'cry for help' is a method "UpdateSiteAndExecute<T>" in ActionBinder.cs. It is the place where the Level 1 and Level 2 cache lookup happens. But where is the level 1 cache stored? Each call site has its own level 1 cache. If you look in CallSite.cs and find CallSite<T>, its first field:

    /// <summary>
    /// Dynamic site
    /// </summary>
    public sealed class CallSite<T> : CallSite {
        /// <summary>
        /// RuleSet - keeps history of the dynamic site
        /// </summary>

        private RuleSet<T> _rules;

        ...

    }

 

is the level 1 cache.

While Level 0 stores the rules compiled into an executable code, the Level 1 cache is just a read only (read only is important because it allows us to do cache lookups without holding locks) list of rules that the dynamic site has seen throughout its life. The delegate which is the level 0 cache is formed from (some of) the is the rules from this level 1 cache. Often from all of them, but not necessarily always.

Now we can go back to the cache lookup code in ActionBinder.UpdateSiteAndExecute<T>. The code we care about is starts here:

//
// Look in the site's local cache first, if we have rules, run those.
// If we find match, the site is polymorphic. Update the delegate in that
// case and keep running.
//

IList<Rule<T>> history = siteRules.GetRules();

 

if (history != null) {
    count = history.Count;
    for (index = 0; index < count; index ++) {
        rule = history[index];

        ...

    }
}

In short, we pull out all the rules from the level 1 cache as a list, iterate through them and the code I replaced with "..." is executing one rule at a time. Again, just like with the level 0 cache, the cache lookup happens by executing the rules.

Here also we run compiled code, but of a slightly different kind. This time they are individual delegates formed by the code pertinent to that one rule only. Good thing about those is that once the rule is created, this delegate never changes so we don't have to re-generate it on future Level 1 (and as we'll see, also level 2) cache lookups.

If we get level 1 cache hit, we update the Level 0 cache by including the rule we just found in the delegate and since we just performed the operation (remember, we executed the rule), we have result of the operation at hand so we are done and can go back to whatever the program was doing. The 'interrupt' (cache miss handling) is over.

Level 2

While level 0 and level 1 caches are stored on the dynamic site itself (as a delegate or as a read only list of rules respectively), the level 2 cache is bigger and is stored on the ActionBinder itself. Its purpose is to span many dynamic sites and allow for rule which was created by cache miss on one dynamic site to propagate to a different dynamic site, thus avoiding need to recreate the rule.

The level 2 cache lookup happens right beneath the Level 1 cache lookup. The code starts here:

//
// Look for applicable rules in the caches
//

Type[] argTypes = CompilerHelpers.GetTypes(args);
applicable = _ruleCache.FindApplicableRules<T>(site.Action, argTypes);

//
// Do we have rules? If so, run those
//

if (applicable != null) {
    count = applicable.Length;
    for (index = 0; index < count; index ++) {
        rule = applicable[index];

        ...

    }
}

Now that is awfully similar to the level 1 cache lookup, isn't it? Get list of applicable rules (granted, this time they come from _ruleCache - the Level 2 cache store), but other than that, we also iterate through the rules and ... no surprise ... execute them one by one until we find the match.

When we do find the match, we add the rule to the Level 1 cache and again, since we found the rule by executing it, the work has been done and we can return back to the program. Note that we don't regenerate the Level 0 cache in this case. There is good reason for it, but we'll talk about that in the future post.

Level 2 cache miss

If level 2 cache miss happens, we need to ask the language binder to create a new rule:

//
// No rule matched, we need to create one
//

for (; ; ) {

    rule = CreateNewRule<T>(context, site.Action, args);

    ...

}

Once the binder creates the new rule, we'll try to execute it. If all goes well we store the rule in the level 1 and 2 caches and since we already have the result of the operation (as we just executed the rule), we are done. However, it can happen that the rule we got from the language binder didn't work (due to an asynchronous update to the type system of the dynamic language that has invalidated the rule). If that happens, we try asking the language binder again...

Once we have matching rule, we store the rule in the caches, return the result and the program can continue executing until the next time it experiences a cache miss, and then we are back to square one (well, level 1 cache anyway).

For next time ...

In the future posts we'll explore:

  • Selection of rules from level 1 cache that form the level 0 cache delegate
  • Matchmakers (a mechanism we use for cache lookups)
  • Cache pruning
  • Changes in the Rule API
  • ...

It's been over a week since we released IronPython 2.0 Beta 1 which also contains the Beta 1 version of DLR. The release includes several favorites of mine in the area of expression trees and compilation, mostly invisible to users of IronPython, but of interest to people developing a DLR based languages.

Unfortunately, the side effect of the release is that most of the samples I put out in the previous posts will no longer work. Even though the ideas remain, some things are shifting a little.

The main difference is probably that what we used to call, confusingly, a CodeBlock, is now called LambdaExpression, and the release sources were snapped right before a checkin went through which made LambdaExpression actually an expression. But don't despair, you can get that change as the latest bits on CodePlex already.

Moreover, to create a lambda expression, one must provide a delegate type. Much of the invisible changes I mentioned before were to ensure that the product of compilation of the LambdaExpression - a delegate - will actually match the signature of the LambdaExpression itself (when DLR deals with closures, it adds an extra parameter to lambdas to carry closures around). Now that the signature of the compiled delegate matches the signature of the lambda expression, the factory method for the LambdaExpression requires delegate, or tries to infer one from a small pre-defined set.

Along with this direction, we are really really close making the whole expression tree fully read only and within days we'll be there completely. Why does it make me so excited? Well, DLR, being a runtime, needs to deal with multi-threaded issues and read only structures don't suffer from threading problems. Also, it is a very sane programming model which guarantees that during compilation the producer of the tree hasn't changed his/her mind and, say, added a for loop somewhere, or changed lexical structure of the lambdas.

I think these are the main big items within the DLR itself, and there's much more coming. In the near future I will post some updated samples to show how to work with the new lambda expression object model and I will also start talking more about the 2nd half of the DLR - the dynamic operations, sites, caches and how all that fits together. We scratched the surface in some early posts, but we glossed over some interesting parts and left few things unexplained.

Until then, happy DLR hacking.

Interesting question came to me ... where can one get (latest) sources of DLR?

Basically, right now there are two sources you can get DLR from:

  • RubyForge
  • CodePlex

RubyForge

RubyForge is where the whole IronRuby project lives and as the development progresses, periodically the IronRuby team pushes the latest code to RubyForge. I am not sure how often this happens, or when it happened last, but having poked around the DLR source code which is part of the RubyForge IronRuby project, it may be couple weeks old, which given the series of recent changes, is too old if you just want to play with DLR directly.

CodePlex

CodePlex is where the IronPython project lives. It is also a place where we publish the Alpha releases of IronPython 2.0 which include corresponding alpha releases of DLR. The latest release of IronPython + DLR was Alpha 8.

Apart from the Alpha releases of IronPython + DLR which happen roughly every month, we publish the source code itself more frequently, and you can download it as often as we upload updated version, which is every one to two weeks. The ultimate goal would be per commit, but realistically it happens about once a week.

Recently there was series of big changes happening in the DLR in areas I spent much of my blogs talking about, so if you are following those and playing with the samples, or even creating your own, your best bet is to use the CodePlex sources that we just published this Monday.

Good luck with your DLR projects and feel free to ask questions.

I am still working on answering the 2nd question. In the previous post and the one before that I answered most of it, but there is one more thing to address:

Problem Two

What if the language syntax doesn't provide good guidance for the placement of the dynamic operations. There are cases where syntax just doesn't provide enough information, for example when the dynamic operation needs so happen in a library function. What then? there is no way to create ActionExpression and let DLR do the code generation which will create DynamicSite and jump start the whole magic...

This last detail is quite easily answered, luckily. We can, in our library function, do the same magic that DLR does via code generation, except we'll do it by hand. To stay with the same example (formatting for output), we can build our function "Print" in a following way where we have an instance of dynamic site manually created and the Print function simply invokes it...

// The instance of the dynamic site used by the "Print"
// method to format the object for output

private static DynamicSite<object, string> _formatter =
    DynamicSite<object, string>.Create(
        DoOperationAction.Make(Operators.CodeRepresentation)
    );

// The Print built-in function

public static void Print(CodeContext context, object o) {
    // Invoke the dynamic site
    string formatted = _formatter.Invoke(context, o);  

    // print
    Console.WriteLine(formatted);
}

Same machinery will kick in as in all the cases we explored so far, the only disadvantage here is that the same dynamic site is shared by all calls into the "Print" method instead of just being shared by all calls that happen at the same location in the source code.

DLR uses this trick internally, you can check out the DynamicOperations.cs for many real world examples. And same goes for Python (Converter.cs, Builtin.cs, PythonCalls.cs, PythonSites.Generated.cs and many more).

In the previous post we explored options to express dynamic behavior of "formatting" an object for output. First option was to use relatively well fitting dynamic operation from the DLR core set - CodeRepresentation. There's another option which we'll look at now.

Using Extension Methods

Instead of using CodeRepresentation, we could 'encode' the dynamic operation of "format for output" as a call to an object's method. It is not that far fetched, actually. Python does just that with the __repr__ method.

Suppose we had this "FormatForPrint" method on each object, our ToyBinder would have very easy job ... find it and call it (create tree that will call it that is). Problem is, of course, that not every object has "FormatForPrint".

Luckily, we - the language implementers - are in complete control here, because we can modify the code in ToyBinder to maintain the correct pretense. If the object being passed in has a type we are interested in, we can simply generate appropriate call to the formatting method, similarly to what we did in the previous post, but with a significant difference.

In the last post, all "Format" methods were in the same place, regardless of the type of the object that they format. That's a good way to look at the problem if we only want to implement formatting. Once we add many other behaviors, we'd have to deal with many classes similar to Formatter to contain all such methods. We could refactor our code in a way that all helper methods relevant to a given type are in the same place. One for formatting, another for ... whatever. This way, once the language action binder sees object of a given type, it has only one place to look at to find all these helper methods.

The is very similar to what already exists in C# and VB ... extension methods. For a given type we can declare a static class which is a container for these extension methods and compiler will make them appear on the types they extend. All of a sudden, types have methods they haven't had before, something we are trying to get our ToyBinder to do.

Let's see if this can possibly work ...

First, the encoding of the dynamic operation. The "print" statement in ToyScript could transform into the following dynamic operation:

Ast.Statement(
    Span,
    Ast.Call(
        typeof(Console).GetMethod(
            "WriteLine", new Type[] { typeof(string) }
        ),
        Ast.Action.InvokeMember(
            SymbolTable.StringToId("FormatForPrint"),
            typeof(string),
            InvokeMemberActionFlags.None,
            new CallSignature(0),
            _expression.Generate(tg)
        )
    )
);

To print means, invoke "FormatForPrint" method on the argument, and pass the resulting string to Console.WriteLine.

If we run the code with this change, we get an exception: "There is no member 'FormatForPrint' on ... object". No surprise. We still have to modify our ToyBinder to do the work. It seems like a lot of coding though, detect the type of the argument, find the corresponding 'extension' and then look for the right method to call.

Luckily for us, DLR understands the notion of extension methods so all we need to do is to actually implement the extension methods and then tell DLR which class (or classes, there may be several of those) contain extension methods for a type. And that should be quite simple.

Extension methods

The implementation of FormatForPrint extension methods for some basic types may look like so:

public static class ObjectExtensions {
    public static string FormatForPrint(object o) {
        return "GENERIC: " + (o != null ? o.ToString() : "(null)");
    }
}

public static class DoubleExtensions {
    public static string FormatForPrint(double d) {
        return "DOUBLE: " + d;
    }
}

public static class DecimalExtensions {
    public static string FormatForPrint(decimal d) {
        return "DECIMAL: " + d;
    }
}

In ToyScript, there are actually already an extension methods for string (StringExtensions) so we can just add one more method:

public static class StringExtensions {
    public static string FormatForPrint(string o) {
        return "STRING: " + o;
    }

    // other extension methods 
    // ...

}

 

The last thing to do is tell DLR about these extensions. There's place for that in the virtual method ActionBinder.GetExtensionTypes(Type). We just need to override it and provide the extensions. This happens in ToyBinder.cs:

protected override IList<Type> GetExtensionTypes(Type t) {
    Type ext;
    if (t == typeof(string)) {
        ext = typeof(StringExtensions);
    } else if (t == typeof(double)) {
        ext = typeof(DoubleExtensions);
    } else if (t == typeof(decimal)) {
        ext = typeof(DecimalExtensions);
    } else {
        ext = typeof(ObjectExtensions);
    }

    return new Type[] { ext };
}

 

 

This is just a trivial implementation, the ultimate version would use some kind of a cache which would get populated as our binder loads assemblies with extensions, based on custom attributes. That's what Python does.

Now what will happen when we run?

  1. ToyScript defines that to print an object, we must dynamically invoke "FormatForPrint" method on it and then send the resulting string to Console.WriteLine.
  2. At runtime, DLR will try to figure out for each object what it means to perform the dynamic operation. Currently the "InvokeMember" means just find a member and then call it.
  3. Via the DLR extension mechanism we extended the basic types with extension methods that DLR will now see and all objects now have "FormatForPrintPrint" methods on them
  4. DLR already knows how to call a .NET method so our extension method will be called like any other .NET method would.

And the best part is that it does work:

image

Summary

The extension methods are pretty powerful mechanism that DLR has to extend existing types with additional functionality and it can certainly be used for the problem at hand (and many others).

The reason I said that there are several good ways to implement what Ales asked but no perfect way (yet) is that ultimately, the perfect solution could be if language could simply produce a custom dynamic operation for the operations that don't seem to fit too well into the pre-defined (and currently fixed) set. Beyond that, we can see that the two alternatives we explored both produce working results.

There is one last issue to solve ... so far, the placement of the dynamic operation was determined from source code. There was keyword "print" which we used as the guide as to where to place the dynamic operations. What if there was no keyword "print" (like in Python), what if it was just like any other function (like it is in JScript). What then? Where would be put the dynamic operations and where would the dynamic site live? We'll look at that problem next time.

Getting now to the second question that Ales asked:

" ... As an example let's take Print function .. where I want to print different data types - integers, strings, decimals etc and each type should be printed differently. Instead of doing the same as you did in previous article, having one method and testing if-elseif-elseif..else  for all the types, is it possible to use ActionExpression for calling a method instead of Ast.Call()? I hope ActionExpression could give better performance and caching for this, but I didn't figure out how to do this..."

Good news is that the answer is yes (you can take advantage of dynamic caching even in situations such as this one), but there's a bit of a bad news, which is that while there are few good ways to do it, the perfect way that we envision is not yet implemented, but hopefully will be soon.

Problem One

The first problem is representing the dynamic operation as an Action, and that is where the answer is not quite perfect yet.

Action in DLR represents encoding of the dynamic operation. You can think of it as a "dynamic instruction" of sorts. And as of right now, DLR only recognizes fixed set of Actions. The main kinds are (look into DynamicAction.cs for the enum and the DynamicAction base class):

  • DoOperation
  • ConvertTo
  • GetMember
  • SetMember
  • DeleteMember
  • InvokeMember
  • Call
  • CreateInstance

Each enum value above then has corresponding class which contains the encoding of the given dynamic operation. For DoOperations, the encoding of the operation is as simple as another enum value (from Operators.cs). There are quite a few of those, including unary and binary operators, slicing, indexing (Get/Set -Item) and many more. The general rule here is that the enum is enough to encode the operation. For our problem, there's one that could somewhat fit, but the fit may not be perfect: CodeRepresentation (inspired by Python's __repr__).

The other kinds of actions are represented via their own classes, depending what data is part of the action's encoding. For example, all *Member actions must contain the name of the member in question, but there is no deep need for actually having 3 classes for Get/Set/Delete, anyway, that's area we are still actively working on and it'll get better.

Invoke/Call/Create instance are little more complicated because they also encode information about the call signature, whether the call includes a dictionary contents of which are mapped to callee's parameters etc. Invoke is combination of "GetMember" and Call:

a.b(c,d,e)

but done all at once. Python, for example, cannot use Invoke because in Python the above must be exactly equivalent to:

temp = a.b
temp(c,d,e)

The Python rules dictate that a.b be evaluated before any of the arguments. Invoke combines these two operations for languages whose semantic allows this.

... But that was a little digression ...

One option we have to encode our "format for printing" dynamic operation is via the DoOperation(CodeRepresentation), another would be to encode it as a call to a method which is to be found on the object (or its type) which will do the encoding. For example Call("FormatForPrinting").

Using DoOperation(CodeRepresentation)

Let's actually try to implement this. ToyScript has a keyword print which we can use to try this approach. Currently the print is transformed into DLR tree as:

Ast.Statement(
    Span,
    Ast.Call(
        typeof(ToyHelpers).GetMethod("Print"),
        Ast.CodeContext(),
        Ast.ConvertHelper(
            _expression.Generate(tg),
            typeof(object)
        )
    )
);

In other words, as a call to ToyHelpers.Print. Instead, we could change that to the following:

Ast.Statement(
    Span,
    Ast.Call(
        typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
        Ast.Action.Operator(Operators.CodeRepresentation,
            typeof(string),
            _expression.Generate(tg)
        )
    )
);

where we take the argument to the print keyword (_expression), perform dynamic operation "CodeRepresentation" on it and simply pass the result to Console.WriteLine(string). Notice that the dynamic operation itself is typed as string which means it will always produce string, allowing for tighter code generation and faster code especially if value types are involved as it can save boxing/unboxing.

Of course, this is the easy part. If we try to run the ToyScript code:

import System

def f(x) {
    print x
}

f(1)
f("Hello")
f(new System.Decimal(10))

we'll get an exception:

image

The reason is that we haven't yet provided instructions to the DLR how to perform the operation. Obviously, the default DLR attempts to do something about what ToyScript requested have failed and the DLR dynamic binder pretty much produced tree which just throws exception:

//
// AST Rule.Test
//

(((.bound $arg0) != .null) && (((.bound $arg0)).(Object.GetType)() == ((Type)Double)))

//
// AST Rule.Target
//

.throw ((RuntimeHelpers.BadArgumentsForOperation)(
    (Operators)CodeRepresentation,
    .new Object[] = {
        (.bound $arg0),
    },
))

We have to go to ToyScript's dynamic binder and give some instructions as to what it means to perform CodeRepresentation. Before we do that, let's actually implement a really simple formatter. A class with bunch of static methods that do the formatting for the types we want to handle explicitly. This may be a trivial first attempt:

public static class Formatter {

    public static string Format(double v) {
        return "DOUBLE: " + v;
    }

    public static string Format(string s) {
        return "STRING: " + s;
    }

    public static string Format(decimal d) {
        return "DECIMAL: " + d;
    }

}

Really simple. Then, in the ToyBinder, we implement the rules for formatting. I put this code into MakeDoRule<T> right after the string addition rule:

if (action.Operation == Operators.CodeRepresentation) {
    Type argumentType = args[0].GetType();

    StandardRule<T> rule = new StandardRule<T>();

    // Create test: arg0 is argumentType
    rule.Test = Ast.TypeIs(
        rule.Parameters[0],
        argumentType
    );

 

    // Create the target:

    // 1. Look for the "Format" method with perfectly matching signature
    MethodInfo mi = typeof(Formatter).GetMethod("Format", new Type[] { argumentType });

    Expression target;

    if (mi != null) {
        // Call the custom formatter
        target = Ast.Call(
            mi,
            Ast.Convert(rule.Parameters[0], argumentType)
        );
    } else {
        // Call the ((object)arg0).ToString()
        target = Ast.Call(
            Ast.Convert(rule.Parameters[0], typeof(object)),
            typeof(object).GetMethod("ToString")
        );
    }

    // Finish the rule and return it
    rule.Target = rule.MakeReturn(this, target);
    return rule;

}

Pretty much what we do here is look into our Formatter class for a perfectly matching overload of "Format". If we find one, good, call that, otherwise fallback to Object.ToString(). Now we can run our code and see output:

image

If we pass something else to our formatting function "f", our binder falls back to Object.ToString. Suppose we add this to our ToyScript script and run again:

f(new System.Collections.ArrayList())

image

And it worked again :) Really simple example, but if we dig into the saved assembly (must run with
-D -X:SaveAssemblies -X:StaticMethods) we'll find the generated code:

image

Note here that I had to play the same trick I played in one of the first posts where if the dynamic site sees a type once and never again, it won't actually generate the more complicated "if" statement. To work around this optimization, I ran the four lines of code twice in a row to get this behavior. In reality, this is really what we don't want programs to do :)

So this approach does work. In the next post we'll explore a second option how to handle the first problem - trying to encode the string formatting as an invocation of a method which each object should have. Of course, we'll hit a problem that not every object has the method we want to invoke. There is no "OurCustomFormat" on Int32 or Double, is there? But as we'll see, DLR has a way to help us with that also.

Then we will need to address the second problem that arises from the question, which we haven't even identified so stay tuned, we'll get to it all.

Until then, happy hacking.

Yesterday a question from one of the readers prompted me to talk a bit more about the DLR trees, a means DLR uses to represent programs. We focused on just couple of nodes, and today won't be much different, except I'll bring a friend in ... LINQ Expression Trees.

DLR Trees

On the quest to discover what the static DLR tree nodes compile into, we ended up doing a bit of work that compilers generally do for us and hand-crafted a DLR tree to represent function performing an integer addition. You can check out the original source code, today I have pretty much the same thing, module few renames:

using Dlr = Microsoft.Scripting.Ast;

...

public delegate int Adder(int a, int b);

...

// Representing a function performing integer addition in DLR
Dlr.Variable var_a = Dlr.Variable.Parameter(SymbolTable.StringToId("a"), typeof(int));
Dlr.Variable var_b = Dlr.Variable.Parameter(SymbolTable.StringToId("b"), typeof(int));

Dlr.CodeBlock dlr_block = Dlr.Ast.CodeBlock(
    "adder",                                    // Function name
    typeof(int),                                // Return type
    Dlr.Ast.Return(                             // Body
        Dlr.Ast.Add(
            Dlr.Ast.Read(var_a),                
            Dlr.Ast.Read(var_b)
        )
    ),
    new Dlr.Variable[] { var_a, var_b },        // Parameters
    new Dlr.Variable[0]                         // Locals (none needed)
);

// Compile into a delegate
Adder
dlr_add = Dlr.TreeCompiler.CompileBlock<Adder>(dlr_block);

// Call the delegate
Console
.WriteLine("DLR: 7 + 11 = {0}", dlr_add(7, 11));

Nothing surprising here, just a recap of something we already know. If we run this code, we get the following output:

image

LINQ Expression Trees

Let's try the same thing with LINQ Expression trees. They have similar object model so the code shouldn't look too different:

using Linq = System.Linq.Expressions;

...

// Representing function performing integer addition in Linq Expression Tree
Linq.ParameterExpression param_a = Linq.Expression.Parameter(typeof(int), "a");
Linq.ParameterExpression param_b = Linq.Expression.Parameter(typeof(int), "b");

Linq.Expression<Adder> linq_lambda_manual = Linq.Expression.Lambda<Adder>(
    Linq.Expression.Add(            // Lambda value
        param_a,
        param_b
    ),
    param_a,                        // params[] of the parameters
    param_b
);

// Compile LINQ lambda into a delegate
Adder linq_add = linq_lambda_manual.Compile();

// And call it
Console
.WriteLine("LINQ: 3 + 4 = {0}", linq_add(3, 4));

Pretty much the same thing. Create parameters, build the lambda, compile and run. When we run now, we get both DLR and LINQ to answer:

image

The Best Part

Well, the best part to this all is that C# compiler has direct support in the language for the LINQ expression trees. Using that we get the ultimate version of the same thing:

// C# Compiler support for LINQ
Linq.Expression<Adder> linq_labda = (a, b) => a + b;
Adder
super_add = linq_labda.Compile();
Console.WriteLine("C# LINQ: 2 + 3 = {0}", super_add(2, 3));

In fact, the code is now so short it is hard to see the actual tree in there, so let me pull it out:

(a, b) => a + b;

The rest is pure compiler magic of C#. Type inference, detection that the lambda is being assigned to Expression<Adder> and therefore the lambda gets represented as tree (quoted), instead of compiled into a delegate directly. Let's run once more with the compiler supported version of LINQ and we get, not surprisingly: