<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Yet Another Language Geek : Compilers</title><link>http://blogs.msdn.com/wesdyer/archive/tags/Compilers/default.aspx</link><description>Tags: Compilers</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Having Trouble with Queries</title><link>http://blogs.msdn.com/wesdyer/archive/2007/01/04/having-trouble-with-queries.aspx</link><pubDate>Thu, 04 Jan 2007 19:55:25 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1411675</guid><dc:creator>wesdyer</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/wesdyer/comments/1411675.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wesdyer/commentrss.aspx?PostID=1411675</wfw:commentRss><description>&lt;p&gt;&lt;strong&gt;A Funny Joke and&amp;nbsp;a Sad Joke&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;There is a &lt;a href="http://paul.merton.ox.ac.uk/science/jokes.html#balloon"&gt;joke&lt;/a&gt; that goes something like this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Two men are hopelessly lost in hot air balloon.&amp;nbsp; Their condition is aggravated by the fact that they are enshrouded by a thicket of fog.&amp;nbsp; When it seems all&amp;nbsp;is lost, suddenly the fog parts and miraculously a man is standing on the ground below.&lt;/p&gt; &lt;p&gt;They shout, "Hey sir!"&amp;nbsp; The man looks up a little bewildered by the sudden appearance of a hot air balloon.&lt;/p&gt; &lt;p&gt;Their hearts take courage and they ask, "Do you know where we are?"&lt;/p&gt; &lt;p&gt;The man on the ground thinks for a moment as the two men above wait in desperation.&lt;/p&gt; &lt;p&gt;Finally, the man looks up as the fog begins to&amp;nbsp;engulf him again&amp;nbsp;and says, "You are in a hot air balloon."&lt;/p&gt; &lt;p&gt;The two men slump into the hot air balloon as their last hope slips away.&amp;nbsp; One of them says to the other, "He must have been a professor."&lt;/p&gt; &lt;p&gt;The other says, "Why do you say that?"&lt;/p&gt; &lt;p&gt;The first responds, "Because what he said was totally correct but absolutely useless."&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;The joke has several variants with lawyer, teacher, engineer,&amp;nbsp;etc. substituted for professor.&amp;nbsp; This joke's punch line also is a very good moral:&amp;nbsp; Don't annoy users by telling them something that is correct but useless.&amp;nbsp; By useless, I mean of no practical value which can include things that are incomprehensible to the average user.&lt;/p&gt; &lt;p&gt;Lets change the story a little bit:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Two programmers are hopelessly behind on an important software project.&amp;nbsp; Their condition is aggravated by the fact that they are having trouble learning to use&amp;nbsp;the new query feature in C# 3.0.&amp;nbsp; They write the following query:&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; var q = from c in Customers&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where c.State == "WA"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select c.ToStrin(); // c.ToStrin() should be c.ToString()&lt;/font&gt; &lt;p&gt;It doesn't seem quite right but they don't know what they did wrong.&lt;/p&gt; &lt;p&gt;They say to each other, "Maybe the compiler's error message will be helpful and give a hint or two at how to use the language feature correctly."&lt;/p&gt; &lt;p&gt;Their hearts take courage and they look at the error message pane in Visual Studio in anticipation&amp;nbsp;thinking to themselves, "What mistake did we make when writing our query?"&lt;/p&gt; &lt;p&gt;The compiler computes for a moment as the two programmers wait in desperation.&lt;/p&gt; &lt;p&gt;Finally, the compiler spits out an error message which says, "The type arguments for method 'System.Linq.Queryable.Select&amp;lt;TSource,TResult&amp;gt;(System.Linq.IQueryable&amp;lt;TSource&amp;gt;, System.Linq.Expressions.Expression&amp;lt;System.Linq.Func&amp;lt;TSource,TResult&amp;gt;&amp;gt;)' cannot be inferred from the usage. Try specifying the type arguments explicitly."&lt;/p&gt; &lt;p&gt;The two programmers slump into their chairs as their last hope slips away.&amp;nbsp; One of them says to the other, "Those C# compiler guys must be studying the &lt;a href="http://erdani.org/publications/better_template_error_messages.html"&gt;C++ template error message&lt;/a&gt;s and the &lt;a href="http://calculist.blogspot.com/2006/11/does-type-inference-have-to-suck.html"&gt;ML type inference error messages&lt;/a&gt;."&lt;/p&gt; &lt;p&gt;The other says, "Why do you say that?"&lt;/p&gt; &lt;p&gt;The first responds, "Because what it said was totally correct but absolutely useless."&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Error messages are&amp;nbsp;tricky to get right and for some reason compilers often have some of the most cryptic error messages.&amp;nbsp; To be fair, the primary purpose of a compiler is to correctly translate a correct program and to reject incorrect programs.&amp;nbsp; But to paraphrase a common saying, computer programming languages are primarily for humans and only incidentally for machines.&amp;nbsp; Maybe&amp;nbsp;the error messages are cryptic&amp;nbsp;because it is a tool written by programmers for programmers or possibly because&amp;nbsp;the push to have more language features or better optimizations outweighs the demand for quality error messages.&amp;nbsp; Whatever the reason is, compilers tend not to be very helpful in providing good error messages.&lt;/p&gt; &lt;p&gt;The C# compiler team devotes a lot of thought and work to getting error messages right.&amp;nbsp; Eric Lippert, one of my teammates, has &lt;a href="http://blogs.msdn.com/ericlippert/archive/2006/07/07/659259.aspx"&gt;a great post&lt;/a&gt; about error messages (&lt;a href="http://blogs.msdn.com/grantri/archive/2004/02/27/81349.aspx"&gt;apparently GrantRi made one too&lt;/a&gt;).&amp;nbsp; Early on in C# 3.0, we focused on getting the language features complete (100% spec compliant) and sometimes we sacrificed performance or the quality of error messages to achieve this.&amp;nbsp; Now later in the product cycle we are&amp;nbsp;focusing on things like the quality of the error messages that we generate, the performance of the C# compiler, and the performance of the generated IL.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;The Challenges of Getting It Right&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;C# 3.0 presents a bunch of new challenges to our goal of getting our users the right product.&amp;nbsp; The language features are much further from the IL that is generated than they ever have been (try writing a small app with a handful of queries then open the compiled assembly in ILDASM or &lt;a href="http://www.aisto.com/roeder/dotnet/"&gt;Reflector&lt;/a&gt; and you will see what I mean).&amp;nbsp; What all of this amounts to is that the distance from when an error&amp;nbsp;introduced in compilation and when it detected has increased making it more difficult to produce very good error messages.&amp;nbsp; Furthermore, C# 3.0 uses type inference quite a bit which makes it even more difficult to produce good error messages.&amp;nbsp; A glance at a few languages that use type inference&amp;nbsp;extensively will show you that there currently seems to be a tradeoff between the power of a type inference algorithm and its ability to produce good error messages.&lt;/p&gt; &lt;p&gt;Consider when an error occurs in a&amp;nbsp;Linq to Objects query (c.Lastname should be c.LastName):&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;var q = from c in Customers&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; join o in Orders on c.ID equals o.CustomerID&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where c.FirstName == "Wes"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; orderby o.Date&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select new { c.Lastname, o };&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;When the query is compiled using an early production compiler,&amp;nbsp;an error message like the following&amp;nbsp;would occur:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;The type arguments for method 'System.Linq.Queryable.Select&amp;lt;TSource,TResult&amp;gt;(System.Linq.IQueryable&amp;lt;TSource&amp;gt;, System.Linq.Expressions.Expression&amp;lt;System.Linq.Func&amp;lt;TSource,TResult&amp;gt;&amp;gt;)' cannot be inferred from the usage. Try specifying the type arguments explicitly.&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Which probably will only serve to evoke a scratch of the head and an indefinite, "Hmm...".&amp;nbsp; This error message is bad for a number of reasons.&amp;nbsp; First what is this talk about type arguments, I don't see any generics in sight.&amp;nbsp; Second, why is the error referring to Queryable when the query is clearly an Enumerable query.&amp;nbsp; Third, what is this Select method that is being referred to?&amp;nbsp; The query doesn't make a single method call.&amp;nbsp; Fourth, why does the error talk about expression trees when the query didn't even have a lambda in it?&amp;nbsp; It gets even worse if the &lt;em&gt;span&lt;/em&gt; of the query is considered.&amp;nbsp; The span for this particular query is the "select" query contextual keyword.&amp;nbsp; Is that where the error occurred?&amp;nbsp;&amp;nbsp;That doesn't seem likely.&lt;/p&gt; &lt;p&gt;It would be so much better if the error message was just&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;'Customer' does not have a member named 'Lastname'.&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;where the span for the error was "c.Lastname" in the select clause.&amp;nbsp; So why not just produce this error message and why produce the first error message?&amp;nbsp; Believe it or not, the first error message actually makes sense if you think about it really hard (no really, it's true!).&amp;nbsp; The error message refers to type arguments and the Select method because the select clause is translated into a call to a Select method which in this case is a generic method.&amp;nbsp; The reason why it refers to Queryable is because the methods that are candidates are all extension methods and one of those extension methods has a Queryable receiver.&amp;nbsp; Finally, the expression in the select clause becomes the body for a generated lambda that is passed to the Select method, but this lambda does not explicitly specify the types of its arguments so they must be inferred.&amp;nbsp; The problem is that no types can be inferred for the arguments such that the body does not have errors and hence we get the type inference errors.&amp;nbsp; I know, I know this still doesn't excuse the error message.&amp;nbsp; While the error message is correct, it is not very helpful.&amp;nbsp; Especially considering that the user should not need to know how queries are translated or how lambdas work or how type inference works and so on.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;A Deeper Look&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;During our third milestone, we did work to make &lt;em&gt;this&lt;/em&gt; error message better.&amp;nbsp; It was the worst of all of our error messages.&amp;nbsp; I can't find words adequate to describe how painful it was to fix very large queries that had simple errors.&lt;/p&gt; &lt;p&gt;The root of the problem lies in producing good error messages for lambdas even though the lambda may be&amp;nbsp;bound&amp;nbsp;several different ways through type inference.&amp;nbsp; Consider the following program:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;using System.Linq;&lt;/font&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;class Program&lt;br&gt;{&lt;br&gt;&amp;nbsp; static void Main(string[] args)&lt;br&gt;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; M(x =&amp;gt; x.FooM());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;case 1&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; M(x =&amp;gt; x.BarM());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // case 2&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; M(x =&amp;gt; x.BazM());&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // case&amp;nbsp;3&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; M(x =&amp;gt; x.FooM() + x.BarM()); // case 4&lt;br&gt;&amp;nbsp; } &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; static void M(Func&amp;lt;Foo, Foo&amp;gt; f)&lt;br&gt;&amp;nbsp; {&lt;br&gt;&amp;nbsp; } &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;&amp;nbsp; static void M(Func&amp;lt;Bar, Bar&amp;gt; f)&lt;br&gt;&amp;nbsp; {&lt;br&gt;&amp;nbsp; }&lt;br&gt;} &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;class Foo&lt;br&gt;{&lt;br&gt;&amp;nbsp; public Foo FooM() { return this; }&lt;br&gt;} &lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;class Bar&lt;br&gt;{&lt;br&gt;&amp;nbsp; public Bar BarM() { return this; }&lt;br&gt;}&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;font face="ver"&gt;In each call to M, a lambda is passed as the only parameter.&amp;nbsp; In each lambda, x could have the type Foo or the type Bar since there are two methods named M which take a parameter type of Foo and a parameter type of Bar respectively.&amp;nbsp; In case 1, if x has type Foo then the lambda binds correctly but if it has type Bar then an error occurs so x must have type Foo.&amp;nbsp; In case 2, we can use the same logic to show that x must have type Bar.&amp;nbsp; In case 3, the lambda has an error whether x is bound having type Foo or type Bar so case 3 is an error.&amp;nbsp; In case 4, we have the same as case 3.&amp;nbsp; So both case 3 and case 4 produce cryptic error messages.&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Let's look at cases 3 and 4 closely.&amp;nbsp; In case 3, when we bind the lambda with type Foo we get one error:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;test.cs(9,18): error CS1061: 'Foo' does not contain a definition for 'BazM' and&lt;br&gt;no extension method 'BazM' accepting a first argument of type 'Foo'&lt;br&gt;could be found (are you missing a using directive or an assembly&lt;br&gt;reference?)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;When we bind the same lambda with type Bar we also get one error:  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;test.cs(9,18): error CS1061: 'Bar' does not contain a definition for 'BazM' and&lt;br&gt;no extension method 'BazM' accepting a first argument of type 'Bar'&lt;br&gt;could be found (are you missing a using directive or an assembly&lt;br&gt;reference?)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Notice&amp;nbsp;the similarity between the two error message?&amp;nbsp; They both have the same error number and the same span.&amp;nbsp; Essentially, the same program text caused&amp;nbsp;the error and both are the same error class.&amp;nbsp; They only differ in the parameters that are used to produce the error text ("Foo" vs "Bar").  &lt;p&gt;In case 4, when we bind the lambda with type Foo we get one error:  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;test.cs(10,29): error CS1061: 'Foo' does not contain a definition for 'BarM' and&lt;br&gt;no extension method 'BarM' accepting a first argument of type 'Foo'&lt;br&gt;could be found (are you missing a using directive or an assembly&lt;br&gt;reference?)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;And when we bind the lambda with type Bar we get one error:  &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;test.cs(10,18): error CS1061: 'Bar' does not contain a definition for 'FooM' and&lt;br&gt;no extension method 'FooM' accepting a first argument of type 'Bar'&lt;br&gt;could be found (are you missing a using directive or an assembly&lt;br&gt;reference?)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This time we have two errors with the same error number but different spans.&amp;nbsp; The difficulty of dealing with the possibly multiple bindings of lambdas and the potentially (very) large number of error messages led to the incomprehensible error message.&amp;nbsp; The sad part is that those more helpful error messages are being hidden (maybe swallowed is a better term) by the more cryptic error.&amp;nbsp; But how can we let them shine through?  &lt;p&gt;&lt;strong&gt;Fixing the Problem&lt;/strong&gt;  &lt;p&gt;We can solve this by introducing two sets of error messages: the union of error messages and the intersection of error messages.&amp;nbsp; Each binding of a lambda produces a set of error messages.&amp;nbsp; In the first such binding of error messages we initialize both the union and intersection sets to be the set of errors produced by the binding.&amp;nbsp; On each subsequent binding we &lt;em&gt;refine&lt;/em&gt; the sets by either taking the union of the binding error message set with the union set or the intersection of the binding error message set with the intersection set.&amp;nbsp; After trying all the error messages, if the intersection set is not empty then we report &lt;em&gt;only&lt;/em&gt; the error messages from that set.&amp;nbsp; Otherwise, we report all of the errors from the union set.&amp;nbsp; For the purposes of set semantics, we define two error messages to be the same iff they have both the same span and the same error number (not necessarily that they have the same text).  &lt;p&gt;The intuition behind this is that the intersection set of errors are those errors that we are sure need to be fixed for the lambda to bind since they are errors under every binding of the lambda.&amp;nbsp; The union errors are possible errors depending on which binding of the lambda is under consideration.&amp;nbsp; So we don't report the union unless the intersection is empty since if they fix all errors in the intersection that may make the program compile correctly (and most often does).&amp;nbsp; In practice, the compiler rarely reports the union set since the intersection set is almost always not empty.&amp;nbsp; In both of the example queries above the intersection set is not empty and so it can be reported.  &lt;p&gt;We are still working on getting the error messages right and would love to have your feedback on other problems that you notice.&amp;nbsp; Next time you see a good error message inside of a query you can now understand how it got there.&amp;nbsp; Or maybe not.&amp;nbsp; Maybe it will be helpful enough that you won't even think of it.&amp;nbsp; If that is the case then we have done our job right.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1411675" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wesdyer/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Queries/default.aspx">Queries</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Compilers/default.aspx">Compilers</category></item><item><title>Transparent Identifiers</title><link>http://blogs.msdn.com/wesdyer/archive/2006/12/22/transparent-identifiers.aspx</link><pubDate>Fri, 22 Dec 2006 22:53:41 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1349374</guid><dc:creator>wesdyer</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/wesdyer/comments/1349374.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wesdyer/commentrss.aspx?PostID=1349374</wfw:commentRss><description>&lt;p&gt;&lt;a href="http://blogs.msdn.com/wesdyer/archive/2006/12/21/comprehending-comprehensions.aspx"&gt;My last post&lt;/a&gt; described in detail how query expressions in C# are translated, but I have a confession to make.&amp;nbsp; I did a little bit of &lt;a href="http://en.wikipedia.org/wiki/Hand_waving"&gt;hand waving&lt;/a&gt; through one part of the translation rules.&amp;nbsp; The astute reader (whomever that is, whenever I read such things I always wonder, "Am I that astute reader?") may have noticed that four of the rules introduced a decidedly foreign language feature into rewrite rules.&amp;nbsp; For example, examine rule #14 closely:&lt;/p&gt; &lt;p&gt;14.&amp;nbsp; &lt;em&gt;From followed by let&lt;/em&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from x1 in e1 let x2 = e2 ...&lt;/font&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from * in e1.Select(x1 =&amp;gt; new { x1, x2 = e2 }) ...&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;What is that asterisk in the resultant transformation and what does it mean?&amp;nbsp; It isn't a pointer deferencing operator&amp;nbsp;and it isn't a multiplication operator, hmmm...What could it be?&amp;nbsp; It looks like it is taking the place of an identifier in the from clause but that is a strange name for an identifier.&lt;/p&gt; &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/wesdyer/WindowsLiveWriter/TransparentIdentifiers_CE3F/image%7B0%7D%5B1%5D.png" atomicselection="true"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="222" src="http://blogs.msdn.com/blogfiles/wesdyer/WindowsLiveWriter/TransparentIdentifiers_CE3F/image%7B0%7D.png" width="240" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;In fact, the asterisk denotes a &lt;em&gt;transparent identifier &lt;/em&gt;and it is the name of this identifier.&amp;nbsp;&amp;nbsp;Transparent identifiers&amp;nbsp;are one of the strangest additions to C# 3.0.&amp;nbsp; The spec has precious little to say about them.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;1.&amp;nbsp; They are introduced to a program only by query rewriting&lt;/p&gt; &lt;p&gt;2.&amp;nbsp; Each transparent identifier has exactly one associated anonymous type (the anonymous type introduced during the same rewrite step as the transparent identifier)&lt;/p&gt; &lt;p&gt;3.&amp;nbsp; When a transparent identifier is in scope, its members are in scope as well (transivitely)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Rule 3 is kind of strange because it mentions &lt;em&gt;scope&lt;/em&gt; in an otherwise syntactic rewrite.&amp;nbsp; &lt;em&gt;Scope&lt;/em&gt; has to do with semantics and not syntax so we have to be very careful how we apply this rule.&amp;nbsp; Now consider the following query:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from x in foo&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;let y = f(x)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;let z = g(x, y)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;select h(x, y, z)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Applying rule 14 the first time the query is reduced to:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from * in foo.Select(x =&amp;gt; new { x, y = f(x) })&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;let z = g(x, y)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;select h(x, y, z)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Here we introduced a transparent identifier and the anonymous type that is associated with it is new { x, y = f(x) }.&amp;nbsp; Now we apply rule 14 again.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from * in foo.Select(x =&amp;gt; new { x, y = f(x) }).Select(* =&amp;gt; new { *, z = g(x, y) })&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;select h(x, y, z)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We have now introduced another transparent identifier but this one is associated with a different anonymous type.&amp;nbsp; It is associated with new { *, z = g(x, y) }.&amp;nbsp; In order to note the difference they will labeled them *1 and *2.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;from *2 in foo.Select(x =&amp;gt; new { x, y = f(x) }).Select(*1 =&amp;gt; new { *1, z = g(x, y) })&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;select h(x, y, z)&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Finally, we can apply rule 15.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;foo.Select(x =&amp;gt; new { x, y = f(x) }).Select(*1 =&amp;gt; new { *1, z = g(x, y) }).Select(*2 =&amp;gt; h(x, y, z))&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Where *1 and *2 are associated with the following anonymous types:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;*1 = new { x, y = f(x) }&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New"&gt;*2 = new { *1, z = g(x, y) }&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;You may notice that in the final select call that there is a lambda with only one parameter (*2) but in the body of this lambda we reference x, y, and z but none of these variables are in scope.&amp;nbsp; Using rule 3 about transparent identifiers we see that when *2 is in scope so&amp;nbsp;are *1 and z but since *1 is now in scope so are x and y (the transitive closure of the members of *2).&amp;nbsp; So really when we refer to x in h(x, y, z) we are really referring to *2.*1.x.&amp;nbsp; Thus we have the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;foo.Select(x =&amp;gt; new { x, y = f(x) }).Select(*1 =&amp;gt; new { *1, z = g(*1.x, *1.y) }).Select(*2 =&amp;gt; h(*2.*1.x, *2.*1.y, *2.z))&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Finally, once we realize that the compiler treats these transparent identifiers just as unspeakable compiler generated names then we see that all the magic has been removed:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;font face="Courier New"&gt;foo.Select(x =&amp;gt; new { x, y = f(x) }).Select(t0 =&amp;gt; new { t0, z = g(t0.x, t0.y) }).Select(t1 =&amp;gt; h(t1.t0.x, t1.t0.y, t1.z))&lt;/font&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Voila!&amp;nbsp; No more transparent identifiers.&amp;nbsp; Transparent identifiers are used in query rewriting to package up the intermediate results and pass them onto the next clause.&amp;nbsp; They are&amp;nbsp;means of essentially creating a little scope and passing it around.&amp;nbsp; It is a fantastic idea that allows variables to flow through the queries providing the kind of behavior that users expect to see.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1349374" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Programming/default.aspx">Programming</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Queries/default.aspx">Queries</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Compilers/default.aspx">Compilers</category></item><item><title>Comprehending Comprehensions</title><link>http://blogs.msdn.com/wesdyer/archive/2006/12/21/comprehending-comprehensions.aspx</link><pubDate>Fri, 22 Dec 2006 01:35:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1342764</guid><dc:creator>wesdyer</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/wesdyer/comments/1342764.aspx</comments><wfw:commentRss>http://blogs.msdn.com/wesdyer/commentrss.aspx?PostID=1342764</wfw:commentRss><description>&lt;P&gt;Not long ago, I was reading through some articles posted on &lt;A href="http://programming.reddit.com/" mce_href="http://programming.reddit.com/"&gt;programming.reddit.com&lt;/A&gt; when I came across an article claiming that &lt;A href="http://blogs.msdn.com/wesdyer/archive/2006/12/20/types-of-confusion.aspx" mce_href="http://blogs.msdn.com/wesdyer/archive/2006/12/20/types-of-confusion.aspx"&gt;C# is trying to be a dynamic language&lt;/A&gt;.&amp;nbsp; One user posted a comment that mentioned that C# 3.0 included among other things "embedded SQL".&amp;nbsp; Unfortunately, it seems that there is still some confusion about what query expressions are.&amp;nbsp; Let's be clear: query expressions are &lt;STRONG&gt;&lt;EM&gt;not&lt;/EM&gt;&lt;/STRONG&gt; embedded SQL.&amp;nbsp; In fact, they are closer to &lt;A href="http://en.wikipedia.org/wiki/List_comprehension" mce_href="http://en.wikipedia.org/wiki/List_comprehension"&gt;list comprehensions&lt;/A&gt; in languages like &lt;A href="http://www.haskell.org/" mce_href="http://www.haskell.org/"&gt;Haskell&lt;/A&gt; and &lt;A href="http://www.python.org/" mce_href="http://www.python.org"&gt;Python&lt;/A&gt; than they are to embedded SQL.&lt;/P&gt;
&lt;P&gt;At a high level, query expressions provide nine basic operations (from, join, join...into, group...by, orderby, where, let, select, into).&amp;nbsp; Each of these basic operations is expressed as a clause.&amp;nbsp; The clauses are then chained together to provide a declaration of query; however, if you only view a query then you can't be sure what the behavior at runtime will be.&amp;nbsp; The reason is that queries are translated into a series of method calls but the methods that are invoked are not predefined methods from the compiler's point of view.&amp;nbsp; The compiler just creates the calls and binds the calls to whatever methods are applicable given the arguments and the receiver of the call.&amp;nbsp; This is both the source of the beauty and the confusion of query expressions.&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Syntactic Sugar&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Query expressions are an interesting language feature because they are &lt;A href="http://en.wikipedia.org/wiki/Syntactic_sugar" mce_href="http://en.wikipedia.org/wiki/Syntactic_sugar"&gt;pure syntactic sugar&lt;/A&gt;.&amp;nbsp; But to really understand queries we need to see what lies beneath.&amp;nbsp; Let's break through the syntactic sugar.&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://www.geomedien.geographie.uni-kiel.de/referenz/lehre/ss2003/geomulti/Weltmarkt/bilder/sugar_cube_shot.jpg" mce_src="http://www.geomedien.geographie.uni-kiel.de/referenz/lehre/ss2003/geomulti/Weltmarkt/bilder/sugar_cube_shot.jpg"&gt;&lt;/P&gt;
&lt;P&gt;Consider the following query:&lt;/P&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;var&lt;/SPAN&gt; q = &lt;SPAN class=kwrd&gt;from&lt;/SPAN&gt; x &lt;SPAN class=kwrd&gt;in&lt;/SPAN&gt; foo&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;where&lt;/SPAN&gt; x &amp;gt; 0&lt;/PRE&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;select&lt;/SPAN&gt; x + 1;&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;&amp;nbsp;This query is transformed into:&lt;/P&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;var&lt;/SPAN&gt; q = foo.Where(x =&amp;gt; x &amp;gt; 0)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;           .Select(x =&amp;gt; x + 1);&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;But what "Where" and "Select" methods are being called?&amp;nbsp; It is impossible to tell from this code snippet; it depends on what the type of "foo" is and which extension methods are in scope.&amp;nbsp; This is the beauty of query expressions and why we can do neat things like &lt;A href="http://msdn.microsoft.com/data/ref/linq/" mce_href="http://msdn.microsoft.com/data/ref/linq/"&gt;LINQ to Objects, DLINQ, and LINQ to Entities&lt;/A&gt; and so on.&amp;nbsp; Each of these providers can author their own "Where" and "Select" methods which would be called if foo was a DLINQ object or a LINQ to entities object.&amp;nbsp; If you wanted the queries to behave differently all you need to do is define your own methods such that the compiler will bind to them when you write a query.&amp;nbsp; The set of methods that needs to be implemented is called the "Query Pattern" and implementers of this pattern are called "Providers".&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Transformation Rules&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Their are (only ;) eighteen rules that define how queries are transformed.&amp;nbsp; Here is how the transformations can be performed given that you have a query (each transformation lists the pattern that to match first and then the resultant transformation on the next line):&lt;/P&gt;
&lt;P&gt;&lt;EM&gt;First remove query continuations.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;1.&amp;nbsp;&amp;nbsp; &lt;EM&gt;If the query has a continuation rewrite it (only rewrite the first one right now)&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 ... into x2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x2 in (from x1 in e1 ...) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;Now transform the query into a list of clauses and for each clause if it is explicit then make it implicit like rules&amp;nbsp;2-4 state:&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;2.&amp;nbsp;&amp;nbsp; &lt;EM&gt;Explicit from&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from T x1 in e1&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1.Cast&amp;lt;T&amp;gt;()&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;3.&amp;nbsp;&amp;nbsp; &lt;EM&gt;Explicit join&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;join T x1 in e1 on k1 equals k2&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;join x1 in e1.Cast&amp;lt;T&amp;gt;() on k1 equals k2&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;4.&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;EM&gt;Explicit join into&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;join T x1 in e1 on k1 equals k2 into g&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;join x1 in e1.Cast&amp;lt;T&amp;gt;() on k1 equals k2 into g&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;Now we check for the degenerate query and rewrite it in a special way.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;5.&amp;nbsp;&amp;nbsp; &lt;EM&gt;Now if the query is degenerate then do a special transform&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 select x1&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.Select(x1 =&amp;gt; x1)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;Finally, now that we have a list of clauses (without explicit types) we simply apply rules 6-18 repeatedly until we don't have a query any more.&amp;nbsp; Think of it as pattern matching.&amp;nbsp; If the list of clauses matches the given pattern then reduce the list given the transformation rule.&amp;nbsp; Rinse and Repeat.&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;6.&amp;nbsp;&amp;nbsp; &lt;EM&gt;Two froms followed by a select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 from x2 in e2 select e3&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.SelectMany(x1 =&amp;gt; e2, (x1, x2) =&amp;gt; e3)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;7.&amp;nbsp;&amp;nbsp; &lt;EM&gt;Two froms followed by a non-select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 from x2 in e2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from * in e1.SelectMany(x1 =&amp;gt; e2, (x1, x2) =&amp;gt; new { x1, x2 }) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;8.&amp;nbsp;&amp;nbsp; &lt;EM&gt;From followed by a join followed by a select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 join x2 in e2 on k1 equals k2 select e3&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.Join(e2, x1 =&amp;gt; k1, x2 =&amp;gt; k2, (x1, x2) =&amp;gt; e3)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&amp;nbsp;9.&amp;nbsp;&amp;nbsp; &lt;EM&gt;From followed by a join followed by a non-select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 join x2 in e2 on k1 equals k2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from * in e1.Join(e2, x1 =&amp;gt; k1, x2 =&amp;gt; k2, (x1, x2) =&amp;gt; new { x1, x2 }) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;10.&amp;nbsp;&amp;nbsp; &lt;EM&gt;From followed by a join with into followed by a select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 join x2 in e2 on k1 equals k2 into g&amp;nbsp;select e3&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.GroupJoin(e2, x1 =&amp;gt; k1, x2 =&amp;gt; k2, (x1, g) =&amp;gt; e3)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;11.&amp;nbsp;&lt;EM&gt; From followed by a join with into followed by a non-select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 join x2 in e2 on k1 equals k2 into g&amp;nbsp;...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from * in e1.GroupJoin(e2, x1 =&amp;gt; k1, x2 =&amp;gt; k2, (x1, g) =&amp;gt; new { x1, g }) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;12.&amp;nbsp;&lt;EM&gt; From followed by orderby (for some number of keys)&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 orderby&amp;nbsp;k1, k2, k3&amp;nbsp;...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1.OrderBy(x1 =&amp;gt; k1).ThenBy(x1 =&amp;gt; k2).ThenBy(x1 =&amp;gt; k3)&amp;nbsp;...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;13.&amp;nbsp;&lt;EM&gt; From followed by where&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 where e2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1.Where(x1 =&amp;gt; e2) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;14.&amp;nbsp; From followed by let&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 let x2 = e2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from * in e1.Select(x1 =&amp;gt; new { x1, x2 = e2 }) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;15.&amp;nbsp; &lt;EM&gt;From followed by non-identity select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 select e2&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.Select(x1 =&amp;gt; e2)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;16.&amp;nbsp; &lt;EM&gt;From followed by identity select&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 select x1&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;17.&amp;nbsp; &lt;EM&gt;From followed by non-identity group by&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 group e2 by e3&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.GroupBy(x1 =&amp;gt; e3, x1 =&amp;gt; e2)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;18.&amp;nbsp; &lt;EM&gt;From followed by identity group by&lt;/EM&gt;&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 group x1 by e2&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.GroupBy(x1 =&amp;gt; e2)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;H3&gt;&lt;STRONG&gt;Example Translation&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Consider the following query:&lt;/P&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;from&lt;/SPAN&gt; x &lt;SPAN class=kwrd&gt;in&lt;/SPAN&gt; foo&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;where&lt;/SPAN&gt; x &amp;gt; 0&lt;/PRE&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;from&lt;/SPAN&gt; y &lt;SPAN class=kwrd&gt;in&lt;/SPAN&gt; bar&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;select&lt;/SPAN&gt; x + y&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;To translate this query, we first remove check to see if rule 1 is applicable (no) and then we translate the query into a list of clauses while applying rules 2-4 (which don't apply to this query).&amp;nbsp; After rules 1-4 we have the following list of clauses.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;[from x in foo, where x &amp;gt; 0, from y in bar, select x + y]&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Rule 5 does not apply because it is not the degenerate identity select.&amp;nbsp; Now we will apply rules 6-18 repeatedly until the query is fully reduced.&amp;nbsp; The rules are always applied to the front of the list and so the first rule that is applicable is rule 13.&amp;nbsp; Which states:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 where e2 ...&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1.Where(x1 =&amp;gt; e2) ...&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Applying this rule the list becomes:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;[from x in foo.Where(x &amp;gt; 0), from y in bar, select x + y]&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now the next rule that is applicable is rule 6 which states:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;from x1 in e1 from x2 in e2 select e3&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;e1.SelectMany(x1 =&amp;gt; e2, (x1, x2) =&amp;gt; e3)&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The list now becomes:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;[foo.Where(x &amp;gt; 0).SelectMany(x =&amp;gt; bar, (x, y) =&amp;gt; x + y)]&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;We no longer have a query so we are done and the final rewrite looks like:&lt;/P&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE class=alt&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;foo.Where(x =&amp;gt; x &amp;gt; 0)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;   .SelectMany(x =&amp;gt; bar, (x, y) =&amp;gt; x + y)&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;Other queries are translated in a similar fashion.&amp;nbsp; As you translate more of these,&amp;nbsp;you will notice that the rules are in fact very simple and can be easily mechanically applied.&amp;nbsp;&amp;nbsp;With some practice, you should be able to translate them quickly in your head much like doing math.&amp;nbsp; It is simply a process of&amp;nbsp;applying the rules in the right order and reaching a fixed point.&lt;/P&gt;
&lt;P&gt;You may have noticed that&amp;nbsp;rule 1 actually creates a nested query.&amp;nbsp; The rewrite rules as I explained them did not remove this nested query.&amp;nbsp; After rewriting a&amp;nbsp;query, recurse and rewrite queries found in its subexpressions.&amp;nbsp; And that is it!&amp;nbsp;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Implications&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Interestingly, the C# spec does not specify much more than this about the semantics of query expressions; however, the rewrite rules &lt;EM&gt;do imply&lt;/EM&gt; some significant details that may not be apparent.&amp;nbsp; Some of these details include scoping rules, error message and debugging behavior, and even hidden new language features like transparent identifiers.&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;&lt;EM&gt;Happy Querying!&lt;/EM&gt;&lt;/STRONG&gt;&lt;/H3&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1342764" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Programming/default.aspx">Programming</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/C_2300_/default.aspx">C#</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Queries/default.aspx">Queries</category><category domain="http://blogs.msdn.com/wesdyer/archive/tags/Compilers/default.aspx">Compilers</category></item></channel></rss>