Microsoft released an update of its upcoming LINQ extensions to .NET. The database support is feeling much more complete.
I noted a proliferation of new keywords to the query comprehension syntax such as join, let, and equal. On the one hand, relational operators have been around for a long time in functional languages and would seem to be natural peers to arithmetic and logic operators standard in virtually every language; the omission of relational operators is primarily do to the lack of support for passing around expressions (either lambda expressions or expression trees) in mainstream languages. On the other hand, the new keywords highlight the poor metaprogramming capabilities in C# and VB such as lack of clean macros.
There’s a new IQueryable support that allows for pluggable query processors, enabling polymorphic and dynamic evaluation of queries. IQueryable differs from IEnumerable in several ways:
- Queries called on IEnumerable run locally, whereas IQueryable may run remotely.
- Extension methods for IEnumerables use lambda expressions; those for IQueryable rely on expression trees, which must later be interpreted or recompiled.
The creation of a new query processor is not entirely obvious to me and feels somewhat complex (perhaps unnecessarily). The methods in IQueryable feel like static methods, having nothing to do with the instance. There are two classes Queryable and QueryExpression, which contain a bunch of static methods for constructing queries, one for IQueryables and another for Expressions.
Support for expression trees have been revamped with the new ability to parse strings to expressions and compile them into IL. I predict a larger role for them in the future, especially since I am already doing work with my own variant of expressions. Expression trees in LINQ feel like a first release, though. One thing they got right was making them immutable. The C# guys probably hedged their bets by only promising to provide the abstract syntax tree; however, the DLinq have clearly more plans for these new critters.
I noted some other issues…
- Expressions don’t support operator overloading, so composing multiple expressions requires explicit constructor calls.
- Expressions are outputted through ToString() without the use of symbols For example, x+y becomes “Add(x,y).” I understand that part of the reason is the different operator text and precedence rules used in the various programming languages. However, I think it would simply be better to emit a standard representation, that can be parsed by the newly available expression parser and can also be easily serialized to a file.
- The expression parser, by the way, doesn’t conform to any specific language and contains a number of bugs. I checked for conformance with VB, SQL, C#/C++.
- The comparison (>=, <=, etc) operators have lower precedence than the equality operators, which doesn’t conform to any language. This doesn’t really make sense, because boolean operations can only be checked for [in]equality, not compared).
Equality normally has lower precedence as in the C-series of languages (C#, C++, etc), or same precedence as in most other languages.
- The precedence for the not operator is low for both the VB-style “Not” and the C#-style “!”. While I think that the not operator should have always been low and the designers of C made a mistake, since boolean values can’t be operated on arithmetically, perhaps for conformance sake the C#-style operator should maintain its original precedence.
- Multiple versions of operators are supported. “<>” and “!=” for inequality. “And” and “Or” are supported as are “AndAlso” and “OrElse.” These all have different expression types in addition to BitAnd and BitOr, resulting in three different versions of And and Or operators. Should shortcircuiting be a distinguishing characteristic, or should the And, Or should actually map to BitAnd and BitOr except for boolean values.