-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: extend the set of things allowed in expression trees #2545
Comments
I think allowing the null operators in expression trees would be one of the most useful, especially in regards to frameworks that provide LINQ support for databases. |
To add to your list, even subscription and unsubscription. |
I believe that supporting statement-bodied lambdas would make expression trees a lot more interesting in many real world scenarios. I posted to the Visual Studio UserVoice site a while back, and it seemed like there is some interest in this. |
+1 for enabling |
Also Invocations that use default or named parameters in expression trees will be nice :) |
I don't think that dynamic would be possible, because it is handled runtime, which would prevent the ability for the expressions to be handled during compilation. |
I am also very much interested in allowing statement-body like @VictorBlomberg for dsl purposes. |
We now have many different ways to represent code as an object model - CodeDom, LINQ Expression Trees, and now Roslyn. Since Roslyn is the most complete of all the models (and will continue to be so) is there anyway it could become the basis for lambda expressions going forward? Perhaps by introducing something like a RoslynExpression (name not important) in order to support backward comparability by not changing Expression (and ecosystem). This way there isn't a continuous need to create parallel representations of lambda expressions to support new language features. That said, a couple things jump out as making this a less ideal approach:
Thoughts? |
@iSynaptic I wouldn't consider the CodeDOM object model for any new code. It doesn't support even some of the most basic things (like static classes). And regarding Roslyn and Expression trees, I would actually like to see expression trees expand to be able to do more things (e.g. that you could generate whole types using them), not the other way around, like you're proposing. I think that Expression trees and Roslyn syntax trees have very different primary use cases and because of that there are significant differences between them. And those differences make the kind of metaprogramming you want to do with Expression trees much harder to do with Roslyn. Some of the differences include:
|
I probably think the Expression Trees have the following huge weaknesses:
|
@hivanov On the other hand great thing about Expressions: it's currently the only way to run "dynamic code" (without writing own interpreter) inside Windows Store Applications as as far as I understand correctly they are even .NET Native compatible. If tendency is to go towards AOT instead of JIT: dynamic types are probably not coming back. Regarding GC issues: I am using a lot of Expression.Compile and never had memory issues with them (maybe due to the caching logic) - I must check this out. And of course: +1 for "null-coalescing operators". |
@mkosieradzki: Regarding |
@axel-habermaier Turns out that the interpreted way is actually faster than the compiled way if the expression is executed less than ~50 times. |
It's not slow. It's good-enough for scenarios I am using it for (scripting inside application). @hivanov |
@mkosieradzki I still stand behind my general idea for the extension of the Expression Trees to be able to generate whole classes or, at least, methods that support "this", so they could be used in TypeBuilders. |
+1 for statement bodied lambdas. My use case for this has always been the ability to write GPU programs against a framework that just compile as expressions in C# and then get hoisted and transpiled to a GPU specific byte code at runtime such as nVidia's CUDA PTX or even higher level stuff like DirectX/OpenGL's shaders or OpenCL via some kind of provider model. |
👍 for all these features. I really love expression trees and it would be nice to have more support from the language. |
@exyi this is actually a really good idea. If you can expose Properties and Methods as expression trees you can factor-out a lot of complexity of LINQ queries in reusable parts. We use a cumbersome convention involving declaring the method body as an static signumsoftware/framework@a7e3699 public class PersonEntity
{
(...)
//How it is today
static Expression<Func<PersonEntity,bool>> IsAmericanExpression = p=>p.Country == "USA";
[ExpressionField]
public bool IsAmerican
{
get { return IsAmericanExpression.Evaluate(this); }
}
//How it could be
[ExpressionDefinition]
public bool IsAmerican => this.Country == "USA";
} @gafter If I do a pull request for this, any chance it will get accepted? |
@olmobrutall +1. We have the same problem. I think that the scenario you are describing is very common in a lot of application that are, for example, Entity Framework based where one want to build a set of expression and reuse them by combining them. |
@olmobrutall It's quite often good idea to steal F# features :). |
@gafter Isn't it planned to make switch(expr) {
case LambdaExpression(UnaryExpression(MemberExpression memberExpr)):
case LambdaExpression(MemberExpression memberExpr):
...
}
// instead of
switch(expr) {
case LambdaExpression { Body is UnaryExpression { Operand is MemberExpression memberExpr } }:
case LambdaExpression { Body is MemberExpression memberExpr }:
...
}
// (I'm using OR patterns, don't mind) Also, is there any |
@alrz we do not currently have any such plan. |
Should the pure and impure expressions be differentiated in C#? May be that will enable some enhancements in the cases of pure expressions. Or this has been done already? |
+1 for more Expression Trees support, especially the null-propagating accessors or statement-bodied lambdas (as far as I know the latter are already possible, except it requires using troublesome Expression Trees API to build them). Wouldn't like these to lag too far behind the existing C# features. On a side note, would it be possible to support async/await with LINQ expressions as well, or is it too much of a stretch? According to this SO thread it'd require a major compiler rewrite, but I'd like to have some official C# devs stance to refer to. ^^' |
Think about the problem conceptually. The await keyword actually generates some syntax precompile which generates a state machine (You can find various levels of detail for this online, but here - for example: http://www.filipekberg.se/2013/01/16/what-does-async-await-generate/). It tracks things like
You wouldn't have an expression so much as a complex set of imperative code expressed using symbols which make it seem functional. It would DEFINITELY require a major compiler rewrite. It would also inject lots of complexity to your code and make it very difficult to debug. |
@bartdesmet has done a bunch of prototype work seemingly related to this issue: https://github.com/bartdesmet/ExpressionFutures/tree/master/CSharpExpressions |
One thing that I've always wished that I could do with expression trees in C# was to be able to define a helper or extension method that could be invoked from within the expression but instead of a 🍝 [ExpressionExpansion(MethodName = nameof(IsBetweenExpression))]
public static bool IsBetween(this int value, int minimum, int maximum) {
return (value >= minimum) && (value <= maximum);
}
public static Expression IsBetweenExpression(Expression value, Expression minimum, Expression maximum) {
return Expression.AndAlso(
Expression.GreaterThanOrEqualTo(value, minimum),
Expression.LessThanOrEqualTo(value, maximum)
);
} |
@HaloFour With #8990 it will resolve into a pattern. So you don't need to worry about the |
I don't see how that proposal is related. I wouldn't be interpreting these expression trees myself, I'd be passing them to some provider like Entity Framework. Unless you're suggesting that I wrap EF and translate the expression trees prior to it then interpreting them. |
@HaloFour Ok I don't know what problem you're trying to solve. |
Dropping a link to a hobby project prototype of this type of work, including support for statement trees, dynamic, async lambdas, and a lot of the newer language constructs (feature complete up until C# 6.0, partial implementation beyond that point). It comes with a Roslyn fork that supports capturing these language constructs in expression trees by binding to the corresponding runtime library that represents https://github.com/bartdesmet/ExpressionFutures/tree/master/CSharpExpressions |
The lack of the null-conditional operator and pattern matching in expressions is getting more annoying by the day. Almost daily, I find myself wanting to use them in EF Core queries, only to be reminded that it's not supported. I realize it's probably going to be a lot of work, but I think it should really be a priority. Right now it's like we have two versions of the language. One that is awesome with plenty of shiny new features, and one that is still stuck in 2008. And it's getting worse for every new C# version... |
For those who also came here looking for expression tree support of null propagating operator |
There's now a proposal by a member of the Entity Framework team to move expression trees forward. See #4727 |
This can't come soon enough. Would really like to be able to use switch expressions in expression trees. I mean, they practically share half their names in common! 😆 |
This needs to be prioritized to be honest, this is one of the most disappointing limitations of the language. |
I stumbled over this issue some days ago, too. @MadsTorgersen can you please explain what is the problem of solving this? |
Breaking existing systems that do not expect these nodes. |
The fact is that the existing systems are waiting for the language change so that they can follow up. |
Why it is not good if existing systems will not understand new expressions (nodes) and will throw exceptions? If I remember correctly some years ago the reason was in complexity and amount of work. Nice to hear, that it is not the main a reason now 👍 |
And actually it's still not the reason for most of the features anyway ‒ |
Also it doesn't sound like a reason. |
They're explicitly different situations. One started out in a world where it wsa not changing, and effectively had a contract over time that it would not break (and, indeed, breaks happened and had to be rolled back because it was a significant issue in practice). The other started in a world where we said explicitly it would change and change often. If we did expression trees a new time, we've said we would go the roslyn model. But that's a new system, not an extension to the existing one. |
To everyone commenting here: You might also want to check out #4727, which contains more discussion about how a feature like this might be implemented 🙂 |
There are many solutions that don't cause such issues. Taking use of existing expression types that have been there for 10 years is a good start. Then, if you want to start anew, why not allowing to bind a lambda to the Roslyn model, in addition to By the way, what kind of issues did the addition of new expression types in .NET 4.0 cause, and how were they solved? |
The problem is not the existence of new expression types (some where added which are not touched by the compiler). For consumers this is quite great because it avoids the need to handle all that... |
Now almost 8 years since this issue was created, numerous new features have been added to C#, yet expressions are still stuck in C# 3.0 or something. This is simply unacceptable at this point. I fail to see why this is not being prioritized or getting any traction whatsoever, given that it's becoming an increasingly evident pain point. Proposals like #4727 also seem to have been effectively abandoned. Any updates? cc @CyrusNajmabadi, @tmat, @alrz, @MadsTorgersen |
Other things have been more important, and have also not have the significant concerns and constraints present here.
No. THere have been no updates. Any updates would be posted here as we do all this development in the open. :) Note: you're welcome to come discuss things more with the team on Discord if you'd like! :) |
More than once I missed the ability to compose expressions or have expression with "holes" to plug in another expression. Especially in the context of Entity Framework I have a lot of cases where queries have common sub expressions, but I cannot extract those into a reusable expression. As a very simplistic example, think public static IQueryable<T> MySpecialFilter<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
return queryable.Where(x => x != null && predicate(x)); // does not compile, because expressions cannot be "called"
} I always naively assumed that this kind thing should be easily expressible in the tree object model by stuffing |
@Tragetaschen |
Hallo @Tragetaschen |
@olmobrutall Or just use LINQKit (NuGet). |
Having just re-read this thread and also this one, all the while carefully considering the concerns and explanations provided by the likes of @CyrusNajmabadi, @333fred, et al. I must say they are making about as much sense as how many "new" C# syntactic features have had support added for them in expression trees over the last decade or so, which is, of course, a glorious zero. Let's consider this comment by @333fred — as I think it succinctly and clearly presents the supposed justification:
(emphasis mine) Sounds reasonable, right? Except that it should take no more than 5 seconds for anyone remotely familiar with C#/.NET's and the surrounding ecosystem's progression over the last couple of years, to realize two things, which immediately obliterate this reasoning: Swap the terms "new node" with "new built-in anything" and you'll get an equally sound argument for never adding any feature at any level in any area to the framework/standard library ever; because "what about existing tools that won't immediately support it?", I guess. It is unbelievably unconvincing. There is literally a fundamental analogy here between any hypothetical new expression nodes, and any new standard constructs in general — with the most notable recent example being what happened with Notice that according to the same logic as the one that we're being provided here as to why expressions are stuck at the C# of 10 years ago, apparently the addition of
It gets even worse, this argument is, in fact, even LESS applicable in the case of expression trees, because all consumers of expression trees (most prominently EF), ALREADY ONLY support a subset of "all possible expression trees". An expression tree consumer not supporting a certain thing at a given time is already the status quo, and as such, fully expected. Funny. Some people in the other thread (e.g. @olmobrutall here) tried to point out the same thing, heck, even some comments on related Stack Overflow questions made this point, and made more basic logical sense than anyone from the other side here. It's truly astonishing. |
I don't think the argument here is simply "they cannot be added because that will break existing libraries", it is "how to add them in a way that both preserves the original meaning and interoperates them with existing code". You could rewrite That being said, there is a myriad of currently unused expression types that are not simply a lowering of other expressions, like |
But those are still two different expressions (even if they might be functionally the same); so it's only natural that they're represented by two distinct expression node types. In normal code, the compiler might lower one to the other, yes, but that's just an implementation detail of the compiler and fundamentally not relevant to this conversation. Expressions reflect what's on the "surface", so to speak, just as you can't just replace |
@xamir82 It is relevant ‒ it is not always apparent whether a lambda is treated as an expression, and someone might easily rewrite it as a part of modernizing the code. Even |
Expression trees support only a subset of the things that could be semantically meaningful. Consider allowing them to support the following constructs:
dynamic
The text was updated successfully, but these errors were encountered: