-
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
Feature request: Implicitly typed lambdas #14
Comments
How do I "up" vote this? |
I think that with this it would be important for C# to support a form of duck-typing delegates based on their signature, otherwise the following wouldn't work since C# would infer that var list = Enumerable.Range(0, 10).ToList();
var predicate = x => x % 2;
var evens = list.FindAll(predicate); |
You can say
but it would be nicer if C# allows this as an implicit conversion. |
This is one that we talk about often. VB has implicit types for delegates (not Func and Action but compiler generated ones), and quite liberal implicit conversions between delegate types. We could do the same in C#, and maybe we should. One concern would be that the relative cost of these conversions might be on the highside, and invisible to users because of the implicit conversions. |
@MadsTorgersen Analyzers could potentially reduce the risk of that. Otherwise, we should consider when/if it's possible to infer the type of the lambda based on its use. It would definitely not be possible to infer the type in certain situations and very expensive (in compilation time and implementation effort) in others, like use outside of the current method body. However, it may be worth simply disallowing these "var" instances for the use in the 99% case and no risk of implicit conversion pollution. |
The idea here is just to make the code more readable. For example: Func<Board, int, int, IEnumerable<Board>> newBoard = (rows, cols, previousGames) => {
// calculate a game board
return new Board(data);
}; It would be better as: var newBoard = (int rows, int cols, IEnumerable<Board> previousGames) => {
// calculate a game board
return new Board(data);
}; It is a small difference, but I think that the types together with parameters are much more readable. My initial idea was not to infer every parameter, but just make the declaration simpler. This syntax is enough and don't need implicit conversions. |
Reminder to everyone; this is what the spec actually has to say about lambda ("Anonymous function") expressions:
For the purposes of this discussion, if you think that a lambda is of type |
Not a perfect solution, but remember that you can create aliases for closed generic type names when they get long: using MyFunc = System.Func<Board, int, int, System.Generic.Collections.IEnumerable<Board>>; The later: MyFunc newBoard = (int rows, int cols, IEnumerable<Board> previousGames) => {
// calculate a game board
return new Board(data);
}; |
I probably didn't express myself well. The point is not forcing the lambda to be Action or Func, but be able to use var a = true;
var b = SomeMethod();
var c = (string x) => x.Length;
var d = () => 1; Looking at this code, there is no reason why It wouldn't matter if the compiler internally created some anonymous delegate signatures. From what you said, it seems that the problem is that the spec states that the way lambdas are interpreted depends on the destination of the value. It may be a delegate or an expression tree. But in this specific case, couldn't it include something like "when a lambda expression is assigned to an implicitly typed variable, it is always evaluated as a delegate"? Remember this is a method-scoped variable primarily used for closures. If you need to do anything else, the previous syntax still works and you can force an expression tree with: Expression<Func<int>> x = () => 1;. |
I think that for this to work really well the runtime would have to be convinced to allow casting between different delegate types that have identical signatures. Then the whole problem of implicit conversions would go away because their cost will be 0. When it comes to invoking a delegate it makes no difference if the delegate type is |
@mikedn, I'm not getting the reason to care about casting or changing the runtime. It is all compilation time checking I'm proposing. The way "var" works is that the compiler uses whatever it sees on the right hand side of the expression. Once the type is set, everything remains the same. When using var, you don't care about the type of "var". If you need a delegate to be of a specific type, just create that with that type. I think this solves most of the problems without creating edge cases. You can already do this pretty easily: var p = new Predicate<string>(s => s.Length > 10); The reason I think it is better to not care about casting is because this: var predicate = (string s) => s.Length > 10;
var filtered = list.FindAll(predicate); to me makes as much sense as this: var ticks = "635556672000000000";
var date = new DateTime(ticks); There shouldn't be any conversion or casting. If you need a long, create a long. If you need a predicate, create a predicate. |
Well, I don't get why you'd think that the predicate/FindAll example doesn't make sense. Perhaps that's because you're used to the way things work now. Let's take a simpler example which doesn't involve generics because they can complicate things: delegate void foo();
delegate void bar(); Is there any difference between foo and bar except the name? Nope, no difference whatsoever. Let's try to use these 2 delegates: static void fn() {
}
static void Main() {
foo f = fn; f(); // works, calls fn
bar b = fn; b(); // works, calls fn
f = b; // doesn't work
} Both delegate types do exactly the same thing, you can delete one of them and replace all its usages with the other, this program will work exactly like before except that This weird limitation doesn't exist in C/C++. Function types simply don't have associated names, |
Another idea: What you actually want are local function declarations. So, why not: int MyFunc()
{
int innerFunc(int x) => x * 2;
return innerFunc(4);
} That's pretty close to how it works in F#. Consider: let MyFunc () =
let innerFunc x = x * 2
innerFunc 4 No one would write |
I see what you mean now. That makes sense. But then I think these are two independent ideas.
To me specifically, I keep bumping into the missing "var id = lambda" syntax much more often. But that is probably just my use case. |
The weird limitation doesn't exist in C/C++ because in those two languages a function pointer is just that, a pointer. In .NET all delegates are separate sealed classes which happen to inherit from MulticastDelegate and provide a specialized I agree, however, that it would be very nice if C# glossed over this detail and made it appear that you could simply assign a variable of one delegate type to a variable of a different delegate type as long as the signatures matched. The problem is that the only way to do this in .NET is to actually bind the new delegate to the Func<int, bool> f1 = i => true;
Predicate<int> f2 = new Predicate2<int>(f1.Invoke); // the same as new Predicate<int>(f1) Invoking the new delegate results in two delegate dispatches each of which has more overhead than a normal method call. Granted, we're talking about fairly minuscule amounts of time but if that delegate is called incredibly frequently, such as in a LINQ query over a large number of elements, it could add up. This is the issue that would be nice to solve in the runtime in order to make similar delegates directly assignable to one another. Note that VB.NET has allowed this relaxed conversion of delegates for some time now by hiding the detail of creating a new delegate and incurring that overhead. |
Erm, I have no idea what "a function pointer is just that, a pointer" is supposed to explain. Isn't a managed reference just that, a reference? The reason why this weird limitation doesn't exist in C/C++ has nothing to do with pointers and I specifically avoided talking about pointers. The reason is that C/C++ simply doesn't allow you to create such equivalent but distinct types in the first place. And I know very well what a delegate is and why the runtime doesn't allow the cast. The point is that it could allow the cast and that would avoid the problem mentioned by Mads Torgersen earlier, the potential cost of the improvised conversions the C# compiler would need to make to get around the runtime limitation. |
You didn't mention pointers, but what's what "function types" are and it's really the closest thing you'll find to a delegate built-into the C/C++ languages, unless you break out functors or other function-like objects, which are mostly syntax candy via operator overloading. I'm not sure what you mean about C/C++ not allowing the creating of equivalent but distinct types. Assuming you used functors you could definitely define two which are equivalent and you can force the compiler to cast between them but the results at runtime will probably not be pretty. The remaining explanation was for the benefit of anyone reading the thread and I apologize if I appeared to be lecturing you. |
A delegate is an actual class instance that holds onto the function pointer and the instance pointer. When a lambda is converted to a delegate, a delegate instance is allocated. To convert to another delegate type you have to construct an instance of the new delegate type. With C++, you just have to cast the function pointer, no allocations involved. This is the cost that Mads was alluding to. Typically, casts in C# do not cause allocations either (except possibly with user-defined conversions and boxing), and object identity is preserved. |
Function types and pointers aren't the same thing. The |
The more I think about it, the more I think that @axel-habermaier has the right idea that these scenarios are better addressed by adding local functions to the language; i.e., method declarations inside of method bodies. To use @HaloFour's predicate example, it would look like this: var list = Enumerable.Range(0, 10).ToList();
bool predicate(x) => x % 2; // local function
var evens = list.FindAll(predicate); That way the delegate isn't even created until the method group One downside is that lambdas have implicit (inferred) return type, whereas named methods today are always required to specify an explicit return type ( var list = Enumerable.Range(0, 10).ToList();
var predicate(x) => x % 2; // inferred return type bool
var evens = list.FindAll(predicate); I think this would address many of the scenarios where a need for implicitly typed lambdas is currently felt, as well as being more generally useful. Thoughts? |
That should work pretty well and I've seen requests for local functions before. They have the advantage that they can be called from the enclosing function without having to pay the cost of delegate creation. I think the main concern with this solution is delegate caching. Should the compiler attempt to cache the delegate in cases like the following? foreach (var list in listOfLists) {
var e = list.FindAll(predicate);
...
} If it doesn't cache then you risk having people coming back and saying: hey, it would be nice to be able to write |
Other than the allocation and invocation of the delegate, what are the other benefits of local functions over lambdas or anonymous delegates? |
@MadsTorgersen: Inferred return types for local function declarations would certainly be interesting. For "real" methods, however, I would not support adding that feature (or else you probably should also add Optimization is certainly an interesting topic when considering local function declarations, as there are many opportunities. Let's go through a couple of cases: int F()
{
int f(int x) => x;
return f(3);
} In that case, the compiler could simply emit a static function into the class which is then called directly. No delegate construction is required. As efficient as it gets. static int y;
int F()
{
int f(int x) => x + y;
return f(3);
} Same as above, as the local function only accesses its own parameters and static variables of the enclosing class. The closure does not require a display class in that case. int y;
int F()
{
int f(int x) => x + y;
return f(3);
} We can simply create an instance method in that case to capture the closure semantics of the instance variable. Again, no delegate needs to be constructed. int F(int y)
{
int f(int x) => x + y;
return f(3);
} This is an interesting case. We either have to instantiate a display class that captures the method parameter. Or we change the signature of f such that y is also passed as an parameter (but that is most likely not always possible, I'd guess). In any case, no delegate construction is required. The invocation of f simply invokes the generated method on the display class. Generally, for small functions like the one above, it might even be beneficial to completely inline them. So the compiler should probably emit them with the When a local function is passed to another function, a delegate must of course be created. In that case, I suggest to use the same rules for caching as for lambdas (i.e., only cache functions without a closure, if I recall correctly). Two other things to consider: Do we want to expose local functions in the metadata explicitly? That is, should the reflection APIs be extended such that Local function declarations probably should support attributes, so that I'm able to write, for instance: int F(int y)
{
[DebuggerHidden]
int f(int x) => x + y;
return f(3);
} |
@paulomorgado: Conceptual clarity. |
Would you care to elaborate on that conceptual clarity? What is more conceptual clear in this:
than here:
? Don't get me wrong, I'm ALL for conceptual clarity. |
@MadsTorgersen I looked over some code that uses lambdas and realized there are two cases where local function definitions would be particularly interesting.
|
Let's be realistic, this has nothing to do with clarity. It's just an attempt at working around delegate issues: allocation costs (and in general, absence of any sort of optimizations) and impossibility of converting between delegates with the same signature. |
@paulomorgado (also @mikedn): Your first example defines a local function Also, look at my F# example again, which highlights the conceptual problem at a syntactical level already: let M () =
let m1 () = 0 // Simple local function
let m2 = fun () -> 0 // The delegate way (*) Semantic in the sense of programming language semantics; the actual output is, of course, the same, disregarding the different heap layout. |
Other than delegate allocations, what's the practical difference? And I'm not even assuming that most of the local functions/anonymous methods are used to close over locals and to pass along as a delegate. I think a lot of assumptions are being made about the implementation. I'm assuming that the difference between a local function and an anonymous method is the lack of the need for a delegate. I assume it will implemented just the same way. Maybe it was a bad decision the name the generated methods based on the type name instead of the function name. |
@paulomorgado: Other than performance, probably none if you don't care about the (admittedly only slightly) simpler semantics. It's just an alternative I mentioned so that we can discuss the potential advantages or disadvantages. It probably all comes down to personal preference. For me, especially coming from F#, local function declarations just make more sense than "named anonymous delegates". It seems like @MadsTorgersen and @sharwell found the idea interesting, so there might be something to it. While I seem to be unable to come up with better reasons other than the somewhat obscure "conceptual clarity" argument and delegate avoidance (which seems to be a goal we can all agree on), @sharwell mentioned recursion as a potential benefit (and rightly so, because the way recursive lambdas work at the moment is just horrible and completely non-intuitive). Do you have any arguments specifically against local function declarations? |
I think the syntax of lambdas as much cleaner than local functions. I know it is the same syntax used for real functions but looking at some code looks like there is a syntax error somewhere. int X() {
int Y() { // is this supposed to be a inner function or I forgot to close braces? Looks like while adding or removing a brace, the compiler could treat the entire class as a function body and start pushing reference errors everywhere in the project, instead of having a clear indication of a missing brace somewhere. Another argument is that to me, looking at an inner function as a "value" rather than a "language construct" seems like a nicer abstraction. It is also a syntax that is already being used, so the learning curve is lower. As I said before, it is just changing That doesn't mean I think it needs to be delegates.The compiler already compiles lambdas in different ways today. That could be just another case. Private static methods sounds like a good solution. In any case, I'd like to say that accessing local method variables is very important, so if local functions means losing that, it wouldn't make any sense. |
That doesn't sound like a good idea to me, because it would mean |
Why not? You can assign a method currently to another variable: private void SomeMethod() { }
Action x = SomeMethod; which is compiled to: Action x = new Action(SomeMethod); Once x is a delegate, both your examples should work. var x = (string s) => s.Length;
// would compile to
private static SomeAnonymousMethod(string s)
{
return s.Length;
}
...
Func<string, int> x = new Func<string, int>(SomeAnonymousMethod);
var y = x; // works fine by type inference
object z = x; // works fine because Delegate extends Object
x.GetType(); // works fine because x is an actual type The only thing with this is that a lambda could not be turned into an expression tree. But I think local function declarations wouldn't either. And if they could, the same solution could be applied to lambdas. But I'm probably missing something here as @MadsTorgersen leans toward local function declarations. |
@nvivo I was quoting the part of your post where you said it doesn't need to be a delegate, so I don't understand how does answering that it's a delegate make sense. Maybe I misunderstood what you meant? Or do you mean that the variable won't be a delegate, unless it has to? That would be weird. |
@svick, there are a lot of proposals here, so it is getting confusing. =) My first idea was to compile @axel-habermaier suggested that instead of that, a lambda could be compiled to a static method sometimes. This is what I referred as compiling to a static method. In this case, the method is compatible with any delegate that matches the signature. And if you are just creating a simple local function that is just called, but there is no reflection or anything fancy going on, the compiler could optimize that to a static method call, which would avoid the delegate at all (and probably match 99% of use cases). My example just showed that even if it is compiled to a static method, the compiler can generate code that allows your examples to work by using delegates. It doesn't need to be all or nothing. Why do you think it would be weird to do that? Sounds like normal compiler optimization to me. |
@axel-habermaier, like I said before, "Don't get me wrong, I'm ALL for conceptual clarity." For those use cases that a delegate is not needed, it makes a lot more sense to have local functions. Recursion is also a valid use case - even if a delegate is needed. So it comes down to:
That's enough for me. @sharwell, how strongly do you feel that closures should not be possible for local functions? |
@MadsTorgersen This still means we would need to infer the type of the local function based on later usage, correct? Or are we still considering VB-style delegate relaxation? |
There seem to be quite a few different proposals here at the same time. As for the conversion, item 4 in #420 should cover it. |
This is actually a compelling feature. |
Add tests for underlining reassignments (second edition)
Currently, it is not possible to do this:
Instead, you need to declare the type before:
C# should just assume these kind of lambdas are either Action or Func and infer the type.
Usually these lambdas are used as helpers for the method implementation, so it really wouldn't matter if a
(string s) => true
is aFunc<string, bool>
instead of aPredicate<string>
as long as the signature matches.The text was updated successfully, but these errors were encountered: