What does Expression.Quote() do that Expression.Constant() can’t already do?

Viewed 12059

Note: I am aware of the earlier question “What is the purpose of LINQ's Expression.Quote method?, but if you read on you will see that it doesn’t answer my question.

I understand what the stated purpose of Expression.Quote() is. However, Expression.Constant() can be used for the same purpose (in addition to all the purposes that Expression.Constant() is already used for). Therefore, I don’t understand why Expression.Quote() is at all required.

To demonstrate this, I have written a quick example where one would customarily use Quote (see the line marked with exclamation points), but I used Constant instead and it worked equally well:

string[] array = { "one", "two", "three" };

// This example constructs an expression tree equivalent to the lambda:
// str => str.AsQueryable().Any(ch => ch == 'e')

Expression<Func<char, bool>> innerLambda = ch => ch == 'e';

var str = Expression.Parameter(typeof(string), "str");
var expr =
    Expression.Lambda<Func<string, bool>>(
        Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(char) },
            Expression.Call(typeof(Queryable), "AsQueryable",
                            new Type[] { typeof(char) }, str),
            // !!!
            Expression.Constant(innerLambda)    // <--- !!!
        ),
        str
    );

// Works like a charm (prints one and three)
foreach (var str in array.AsQueryable().Where(expr))
    Console.WriteLine(str);

The output of expr.ToString() is the same for both, too (whether I use Constant or Quote).

Given the above observations, it appears that Expression.Quote() is redundant. The C# compiler could have been made to compile nested lambda expressions into an expression tree involving Expression.Constant() instead of Expression.Quote(), and any LINQ query provider that wants to process expression trees into some other query language (such as SQL) could look out for a ConstantExpression with type Expression<TDelegate> instead of a UnaryExpression with the special Quote node type, and everything else would be the same.

What am I missing? Why was Expression.Quote() and the special Quote node type for UnaryExpression invented?

5 Answers

After this a really excellent answer, it's clear what are the semantics. It's not so clear why they are designed that way, consider:

Expression.Lambda(Expression.Add(ps, pt));

When this lambda is compiled and invoked it evaluates the inner expression and returns the result. Inner expression here is an addition, so the ps+pt is evaluated and the result is returned. Following this logic, the following expression:

Expression.Lambda(
    Expression.Lambda(
              Expression.Add(ps, pt),
            pt), ps);

should return an inner's lambda compiled method reference when the outer lambda is invoked (because we say that lambda compiles to a method reference). So why do we need a Quote?! To differentiate the case when the method reference is returned vs. the result of that reference invocation.

Specifically:

let f = Func<...>
return f; vs. return f(...);

Due to some reason .Net designers chose Expression.Quote(f) for the first case and plain f for the second. To my view this causes a great deal of confusion, since in most programming languages returning a value is direct (no need for Quote or any other operation), but invocation does require extra writing (parentheses + arguments), which translates to some kind of invoke at MSIL level. .Net designers made it the opposite for the expression trees. Would be interesting to know the reason.

I believe it is more like given:

Expression<Func<Func<int>>> f = () => () => 2;

Your tree is Expression.Lambda(Expression.Lambda) and f represents the Expression Tree for a lambda that returns a Func<int> that returns 2.

But if what you wanted was a lambda that returns the Expression Tree for a lambda that returns 2, then you need:

Expression<Func<Expression<Func<int>>>> f = () => () => 2;

And now your tree is Expression.Lambda(Expression.Quote(Expression.Lambda)) and f represents the Expression Tree for a lambda that returns an Expression<Func<int>> that is the Expression Tree for a Func<int> that returns 2.

Related