-
Notifications
You must be signed in to change notification settings - Fork 4.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] Non-Capturing Lambdas with ==> #11620
Comments
While it would involve more syntax I kind of think that such a problem could easily be handled by analyzers. An assembly or class could be annotated with attributes to specify whether capturing lambdas are allowed, and then additional attributes could be applied to specific methods to white/blacklist capturing: [assembly: AllowCaptures(false)]
static class Program {
static void Foo() {
int max = 5;
int[] array = Enumerable.Range(0, 10).Where(i => i < max).ToArray(); // analyzer error
}
[AllowCaptures(true)]
static void Bar() {
int max = 5;
int[] array = Enumerable.Range(0, 10).Where(i => i < max).ToArray(); // good
}
} It's more verbose and the granularity could only be at the method level, but it does achieve the desired goal. |
I am not sure method granularity is adequate. Though I agree this would be possible to prototype without parser changes, which is a significant benefit. I also agree that in many cases, one does want to ensure that all lambdas in a given code region don't capture, so perhaps it would be a good experiment to try -- especially accompanied by reliable static lifting of non-capturing lambdas. |
Very few code bases are so allocation sensitive that this warning would be of any use. This feature is too niche for the C# language. Wasn't there a proposal to allow putting compiletime-only attributes anywhere? This could be implemented through static analysis looking for such attributes. |
I think i agree that this is too niche. If your hot path is so allocation sensitive then i think it is reasonable to fallback to more basic language constructs that involve less compiler magic. |
I don't see the benefit offered by this. The value will be captured if it is used. If it is used, it is needed. In other words you very likely need to write a different function with different semantics. I also dislike the idea of introducing a new syntax for anonymous methods, and less powerful anonymous methods at that. |
I really like how lambdas work in C++ and really want it to work like this. In C++ I have full control over what gets captured and what doesn't and how I want it either by value or by reference, I'm biased but I'm sure that new comers to the C++ syntax (or capture list) will have no problem to pick it up simply because it's explicit and very clear, not to mention more powerful. Adding extra character doesn't really tell me anything about what it does but annoys me, it's ugly, at least in my opinion. |
@AlgorithmsAreCool yeah, I know about this, thanks! :) Just thought to share my opinion about this suggestion. |
A workaround that can be used to avoid capturing is to define and return the lambda from at static method and pass all variables to the method. Then only the passed variables will be captured. Example (a bit contrived but I have used a similar version to solve over capture)
|
I would like a thin arrow |
I still do not see any reason to have this in the language at all. It adds syntactic complexity and noise to introduce a less expressive form of lambdas. If you do not want to close over any outer lexical bindings, do not use them in the lambda. If you use the value it is needed. |
The |
I don't understand, an empty capture list indicates a non-capturing lambda |
If we had capture lists, then no, we would not. The overall point of the proposal was to allow the programmer to express that the lambda should not capture, so that any inadvertent capture would be flagged as an error. The assumption is that non-capturing lambdas are an important enough case (due to their ability to be statically allocated) that special support to facilitate their use is warranted. "Very few code bases are so allocation sensitive that this warning would be of any use. This feature is too niche for the C# language." Perhaps so. In the code bases I personally work with (systems code, including games and browser components), performance demands and allocation-sensitivity are both very high. The C# language's reach is broadening, and the point of this feature is to make this scenario less niche. |
I'm pushing for non-capturing lambdas and this is why: http://stackoverflow.com/q/43217853/1236397 Since C# 6, people shouldn't be forced to push all static lambdas for expression building now into the class making clutter. I vote for either some non-capturing syntax, or the "static" modifier, which is basically much the same thing. |
I'll go ahead and close this discussion. Feel free to continue on csharplang. |
A well-known issue in C# programming is inadvertent lambda capture, in which a lambda, which was intended to be statically allocated, mistakenly closes over some local state -- which forces a full display class allocation and delegate allocation. ReSharper evidently has a warning for this case, but it is as often intentional as not.
Some way for the programmer to express their intent -- that a particular lambda not close over local state -- is needed.
#117 is a proposal to bring full-fledged C++-like lambda capture lists to C#. This would solve the problem, but at the cost of extra syntax for all lambdas, and a maintenance burden for lambdas which do capture (proportional to the number of locals captured).
The main distinction that is needed is binary: is this lambda intended to be capturing, or not?
This proposal is to introduce a new token to the language when defining lambdas: the ==> token. The ==> token, when used in a lambda definition in place of =>, indicates "this lambda must not capture local state," and causes a compiler error if there are any open variables in the lambda.
So in the case of
intEnumerable.Where(i ==> i > 0).Select(i ==> i.ToString())
both of the lambdas would be fine, as neither one captures local state.
But this would be an error:
intEnumerable.Where(i ==> i > intEnumerable.Count)
as the lambda is defined with ==> but references a local variable.
(The motivation for "==>" as the token is that it is obviously close to the existing syntax, in fact so close as to be almost completely unobtrusive (and hence widely usable), while hopefully fitting reasonably well into the existing tokenization rules of the language.)
As far as expressing this in the IL, it is possible that this syntax could result in applying an attribute to the generated method for the lambda, as suggested under item 6 of #1898
The implementation intent is that the compiler reliably lifts ==> lambdas into static allocations, with no runtime allocation cost required when invoking them.
The text was updated successfully, but these errors were encountered: