This project is read-only.

Infer Func/Action for delegates where no other delegate type is implied

Topics: C# Language Design
Apr 10, 2014 at 3:59 PM
Edited Apr 10, 2014 at 4:02 PM
C# infers the type of anonymous methods from context where possible. For example, when passing a lambda to a method, the method parameter type is used as the context for the inference. Same with assignments:
    Func<int> f = () => 5; 
This, however, is illegal:
    var f = () => 5; // cannot assign lambda expr. to implicitly-typed local
    Delegate f = () => 5; // cannot convert lambda expr. to Delegate
The latter use case occurs particularly often when calling older APIs, which specify "Delegate" as the parameter type.

This proposal is for C# to automatically infer a suitable Func<> or Action<> type where the currently present resolution fails to find a suitable type.

Details

C# already implements a powerful enough type inference mechanism to make this possible. In particular, inferring that the return type in the examples above is "int" is not trivial, but it is already implemented:
    public static Func<T> Lambda<T>(Func<T> method) { return method; }

    var f = Lambda(() => 5); // Func<int> inferred
Where the anonymous method takes arguments, the types of the arguments must be specified. So under this proposal, the following is the case:
    var f = x => 5.0 * x;  // error: cannot infer type of lambda parameter "x"
    var f = (long x) => 5.0 * x; // Func<long, double> inferred
This proposal has been brought up before. It seems the time is right to re-evaluate it.

The "Lambda" work-around

It is currently possible to define a complete set of "Lambda" methods as shown above, however this approach is not ideal: because delegate types are not implicitly convertible into other signature-compatible delegate types, one has to make sure to use Lambda only when necessary, and not at other times. For example:
    new Thread(s => { ... }); // legal
    new Thread(Lambda(s => { ... })); // cannot convert Action<T> to ThreadStart
Being required to use Lambda (or explicit typing, or the Action/Func constructors) in some cases, while also being required not to use them at other times makes the language unnecessarily complex.

This proposal makes it possible to always use a "bare" anonymous method, in any context.
Apr 10, 2014 at 4:55 PM
Edited Apr 10, 2014 at 4:57 PM
This proposal is more complex than it seems on the surface.
  • How do you decide what delegate type should be used if multiple compatible ones are in scope?
    • The specification could mandate that it must be in the System namespace, must be called Action<...> or Func<...>, and must be in scope.
  • What do you do if such a delegate type is not available in the System namespace? This would apply to any lambdas with out/ref parameters or with more than 16 parameters.
    • Option 1: Make it a compile-time error. You wouldn’t be able to use bare lambdas with ref/out parameters except in cases where you can already do it now.
    • Option 2: The compiler automatically declares an “anonymous” delegate type in your assembly (similar to anonymous classes) for every type/refness/return-type combination.
Given that it already has these issues, I think we should go a (very small in comparison) step further. I think most of us agree that the idea of “semantic typing” hasn’t shown much benefit for delegates. In other words, it would be nice if compatible delegate types were implicitly convertible between each other. I should be able to pass an Action to new Thread() even if it calls it a ThreadStart.

This approach has the advantage that we do not need the spec to rely on System.Action<> and System.Func<>. In cases where you pass a lambda to a method that expects a specific delegate type, the compiler would do the same as before: use the delegate type specified by the method’s parameter declaration. Only in the new cases, where no delegate type is specified, will the compiler need to generate anonymous delegate types. (This applies to implicitly-typed variable declarations; implicitly-typed arrays; parameters of type Delegate or object; and possibly other stuff I haven’t thought of.)

Rules for conversion between delegates already exist for delegate constructor calls. For example, if I have a Predicate<object> called ps, I can write new Func<object, bool>(ps) to convert it. However, I would like to propose that we allow even more conversion. Delegates are currently covariant and contravariant only if the delegate declaration explicitly makes them so, and then they are co-/contravariant only between different instantiations of the same generic delegate type. Thus, you can implicitly cast Func<object, bool> to Func<string, bool>, but not Predicate<object> to Func<string, bool> (you can do it with the constructor invocation, but only because Predicate is covariant). I propose that we make delegates always co-/contravariant where appropriate (i.e. covariant for parameter types that are not out/ref parameters, and contravariant for the return type).
Apr 10, 2014 at 5:27 PM
While I'm all for implicit conversions between compatible delegate types, that sounds like it has its own set of difficulties. Perhaps the inference of Func<> and Action<> could be one of those features that goes 80% of the way for 20% of the effort. It won't have support for lambdas with out/ref, or >16 arguments, but it would support the common use cases.
Apr 15, 2014 at 12:39 AM
This is a feature idea that we've looked at a lot in the past, but haven't really found a satisfactory solution to, for many of the reasons also listed here.

VB has more compatibility between delegate types, and infers a fresh delegate type for lambdas where one cannot be inferred. It is nice, but can easily lead to unintended wrapping of delegates.
Apr 22, 2014 at 2:09 PM
I'd be happy if C# defaulted to the appropriate Action<> or Func<> delegate type, requiring explicit casting in case anything else is required. After all, this is also what happens when you write var i = 0. The literal 0 could have any numeric type (int, float, decimal, etc.), but you default to int. If you want anything else, either use a cast, a literal suffix or an explicit type. For lambdas, you could do the same thing (except for literal suffixes). So that would help making things more consistent while at the same time simplifying the declaration of local functions, which is something that occurs more often now that we move to a more functional programming style.
Apr 23, 2014 at 2:32 PM
Edited Apr 23, 2014 at 2:34 PM
I think @Timwi's suggestions make sense. From using TypeScript I definitely see the power of structural typing and it would be very useful for delegates in C#.
It is nice, but can easily lead to unintended wrapping of delegates.
True, but I think inferring a delegate type for lambdas is high value enough that it's worth the potential cost. Closures were also something that were a potential pitfall for novices that could result in unintended consequences, but the benefit of adding it to the language far outweighed the costs. Tools like ReSharper warn you about possible unintended closures; I'd assume a tool could do something similar for a potentially unintended wrapping of a delegate.
Jun 20, 2014 at 7:56 PM
rstarkov wrote:
While I'm all for implicit conversions between compatible delegate types, that sounds like it has its own set of difficulties. Perhaps the inference of Func<> and Action<> could be one of those features that goes 80% of the way for 20% of the effort. It won't have support for lambdas with out/ref, or >16 arguments, but it would support the common use cases.
What would be most helpful IMHO, though it would require a CLR change, would be to modify the type loader so that every delegate would be deemed to inherit from a system type whose name was based upon arity and ref usage (e.g. all four-parameter value-returning delegates whose third parameter was passed by ref would be regarded as inheriting from System.Delegates.FuncVVRV<in T1, in T2, T3, in T4, out TResult>). Code which wants a Predicate<string> could only be passed a Predicate<string>, but code which wants a function which can accept a string and return a boolean could use a parameter of type System.Delegates.FuncV<string, bool> to accept something of that type, Func<string, bool>, Predicate<string>, or any other function that accepts a string and returns a bool. The problems of combining heterogeneous delegates could be resolved by adding to each type static Combine and Remove methods which would accept any combination of delegates deriving from its type, and return a delegate of the type upon which the method was invoked.
Jun 22, 2014 at 1:34 AM
While I'm all for implicit conversions between compatible delegate types, that sounds like it has its own set of difficulties. Perhaps the inference of Func<> and Action<> could be one of those features that goes 80% of the way for 20% of the effort. It won't have support for lambdas with out/ref, or >16 arguments, but it would support the common use cases.
I think this is the right idea. And if it will match compatible delegates based on name prefix, e.g. System.Func* and System.Action*, you could support ref/out easily by providing a third-party NuGet package with a lot of generated delegate types (and var would fail at compile-time if here no compatible delegate type).

Later on CLR conversion rules might be improved, but I would mostly use var f = () => ... in script mode or for locals anyway, so conversion does not matter much.
Jun 23, 2014 at 10:54 AM
One can define his own delegate type in System.* namespaces (just like when you are using extension methods and targeting .NET 2.0), so the limitations of current mscorlib shouldn't be an issue.
Jun 28, 2014 at 2:34 PM
Edited Jun 28, 2014 at 2:35 PM
ashmind wrote:
And if it will match compatible delegates based on name prefix, e.g. System.Func* and System.Action*, you could support ref/out easily by providing a third-party NuGet package with a lot of generated delegate types (and var would fail at compile-time if here no compatible delegate type).
Actually scratch that, I think the easiest/best path would be:
  1. No support for ref/out (how often did anyone even use that in lambdas?)
  2. var is interpreted as System.Func<X,Y,...,R> if lambda returns a value, and System.Action<X,Y,...> otherwise.
  3. If corresponding type does not exist in any referenced assembly, a compiler error is produced.
Jun 28, 2014 at 4:28 PM
@ashmind: That would be a huge improvement of C# if what you described was supported. Ever since lambdas and var was introduced I've been wondering why var f = () => {} is not allowed. I've read Eric Lippert's blog post explaining the reasons, but they did not strike me as valid. After all, 0 also has no type. It might be an int, a decimal, a double, etc. But var i = 0 is allowed, implicitly converting 0 to an int. I don't see a reason why the same reasoning can't be applied to lambdas as well. They have no type and can therefore be assigned to a variety of delegate types. But when used with var, by default, it corresponds to Action<> or Func<> with the appropriate generic type arguments. If you want something else, either don't use var or add a cast -- same as for 0 if you want it to be a short, for instance. I'd really love to see that feature in C# 6!
Jun 28, 2014 at 10:00 PM
Expandable wrote:
After all, 0 also has no type.
Are you sure about that? Isn't 0's type int? Isn't the zero double constant 0.0 and decimal 0m?
Jun 29, 2014 at 12:34 AM
VladD wrote:
Are you sure about that? Isn't 0's type int? Isn't the zero double constant 0.0 and decimal 0m?
True, but you can also write decimal d = 0 or double d = 0 or byte b = 0. In that sense, 0 and lambdas are equivalent as they can be implicitly converted to many different types (that are not implicitly interchangeable), but in conjunction with var there is a sensible default type. In case of 0 that type is int, for lambdas it could be one of the Action or Func delegates.
Jun 29, 2014 at 12:45 AM
Expandable wrote:
In that sense, 0 and lambdas are equivalent as they can be implicitly converted to many different types (that are not implicitly interchangeable), but in conjunction with var there is a sensible default type. In case of 0 that type is int, for lambdas it could be one of the Action or Func delegates.
Okay, so 0 has intrinsic type int, and (as any int) is just implicitly convertible to double. If we would choose this way for lambdas, what should be the type for x => {}? If it's, say, Action<int>, the type Action<int> then must be implicitly convertible to Action<bool> and Action<string> and so on. This doesn't look like a desirable change in the language semantics.
Jun 29, 2014 at 12:53 AM
Obviously, var f = x => {} should be a compile time error as the type of x is unspecified and cannot be determined in any other way. So you have to write var f = (int x) => {} so that f can be inferred to be of type Action<int>. I don't see why that should be implicitly convertible to Action<bool> or anything else.
Jun 29, 2014 at 11:55 AM
Ok, so let me elaborate on that. There are two point of views from which we can approach the problem:

1) Lambda functions currently have no type in C#. They are implicitly convertible to all "compatible" delegate types as well as to System.Linq.Expressions.Expression types. For instance, all of the following is valid:
Func<int, bool> f1 = x => x  > 4;
Expression<Func<int, bool>> f2 = x => x > 4;
Predicate<int> f3 = x => x > 4;
Similarly, we could regard 0 (or 1, or 2, etc, basically all integer literals) as having no type. They are implicitly convertible to many different integer and non-integer types. For instance, all of the following is valid:
int i = 0;
byte b = 0;
uint u = 0;
double d = 0;
So, the question that we now have to answer is: What type should be implicitly inferred when var is used to declare the variable? The C# team decided to disallow that in case of lambda functions. On the other hand, they allowed it for integers. They chose a default that is not always correct, but most of the time it does what you want: var i = 0; infers i to be of type int. Now in my opinion, why not apply the same reasoning to lambdas? Sure, defaulting to one of the System.Func<> or System.Action<> delegate types is not always what you want, but in most cases, it is, similar to defaulting to int in the case of integers. And you can even use a similar approach to get an implicitly typed variable with a non-default inferred type by using casts, delegate creation expression or literal suffixes:
var f1 = (int x) => x > 4; // infers f1 to be of type Func<int, bool>
                           // note that the parameter type probably cannot be inferred in this case and must be explicitly stated
var f2 = (Predicate<int>)x => x > 4 // Cast
var f3 = new Predicate<int>(x => x > 4) // Delegate creation expression
var i = 0; // infers i to be of type int
var u = 0u; // infers u to be of type uint using a suffix
var d = 0.0d; // infers d to be of type double using a suffix
var b = (byte)0; // infers b to be of type byte using a cast 
So from that point of view, I don't think that there should be any difference between integer literals and lambda functions (we could maybe even call them lambda literals). There is a sensible default for type inference that does the right thing most of the time. In all other cases, explicitly state what you want. Basically it boils down to: Why did the C# team add a special rule for type inference for implicitly typed variables for integer literals, but not for lambdas? Seems somewhat arbitrary.

2) Let's now say that both lambda functions and integer literals have a default type: Integer literals have a default type of int and lambda functions have a default type of System.Func<> or System.Action<>. Now there doesn't have to be a special rule for implicitly typed variables:
var i = 0; // i is obviously of type int, as that is the type of integer literals
var f = (int x) => x > 4; // f is obviously of type Func<int, bool>, as that is the type of the lambda
Note that there are again special rules that the C# team added to the language: Implicit conversions for literals:
void F(byte b) {...}
F(0); // 0 is of type int, but is in this case implicitly converted to byte
IQueryable<...> q = ...;
q.Where(x => x > 4); // The lambda is of type Func<int, bool>, but can be implicitly converted to Expression<Func<int, bool>>
Just as above, explicit conversions also work for implicitly typed variables:
var f = (Expression<Func<int, bool>>)x => x > 4;
var b = (byte)0;
On the other hand, implicit conversions only apply to literals: Both integer literals and lambda literals might be implicitly convertible to many different types, but variables of those types are not:
var i = 0; // i is int
byte b = i; // Compiler error
var f = (int x) => x > 4; // f is Func<int, bool>
Predicate<bool> p = f; // Compiler error
So also from this point of view, lambdas and integers can -- and in my opinion should -- be treated in the same way.

In any case, the default inferred type should also be used when resolving invocations of overloaded methods:
void F(int i){}
void F(byte b){}
void F(Func<int, bool> f){}
void F(Expression<Func<int, bool>> f){}
// Invocations:
F(0); // Invokes F(int), already working in C# 5
F(x => x > 4); // Invokes F(Func<int, bool>), ambiguous call error in C# 5
Note that allowing the call to F succeed is not a breaking change, as all current code relying on this overload resolution doesn't compile at the moment. It does, however, both alleviate and worsen the brittle base class problem: If a base class defines F(Func<int, bool>) and a later version adds F(Expression<Func<int, bool>>) all client code would fail to compile. With the new approach, F(Func<int, bool>) would still be called. On the other hand, if a base class defines F(Expression<Func<int, bool>>) and later adds F(Func<int, bool>), the new method would be called instead. However, that's also the case for the F(int) and F(byte) functions, so we're already used to that.
Jun 29, 2014 at 10:07 PM
I agree with everyone else in the thread and particularly with Expandable's clarifications above.

Just as var i = 0 could be invalid but isn't because it's so useful to pretend that you'd written int, var c = () => 0 should be made valid by pretending that you'd written Func<int>. None of them make it impossible to simply define the right type if you want something else and both of them can fall off a cliff if you specify something they can't deduce. It just pulls some previously invalid programs into validity where the intent is safe enough to assume. If this was rejected before because it wasn't tractable but with Roslyn is now tractable, it's a feature I wouldn't mind done.
Jun 30, 2014 at 5:32 PM
Expandable wrote:
And you can even use a similar approach to get an implicitly typed variable with a non-default inferred type by using casts, delegate creation expression or literal suffixes:
``C#
var i = 0; // infers i to be of type int
var u = 0u; // infers u to be of type uint using a suffix
How about var x=unchecked(u-i);? Try it if i is a const equal to 0, +1 or -1. Interesting?
So from that point of view, I don't think that there should be any difference between integer literals and lambda functions (we could maybe even call them lambda literals). There is a sensible default for type inference that does the right thing most of the time. In all other cases, explicitly state what you want. Basically it boils down to: Why did the C# team add a special rule for type inference for implicitly typed variables for integer literals, but not for lambdas? Seems somewhat arbitrary.
I don't particularly like the rules for integers, but they're not arbitrary. Type inference is allowed for numeric types in precisely cases where the right hand expression would evaluate to an expression of a particular integer type. There is no concept in the type system of "function which takes parameters X, Y and returns Z". For the compiler to assume Action<T1,T2> given a method with two arguments would make something like the following fail:
void dummyHandler(object sender, EventArgs e) { }
var h0 = dummyHandler;
EventHandler<CatEventArgs> h1 = h0;
EventHandler<DogEventArgs> h2 = h0;
Console.WriteLine('{0} {1}", Object.ReferenceEquals(h0, h1), Object.ReferenceEquals(h0, h2));
Because Event<T> is not contravariant, there is no type that could be inferred for h0 which would be assignment-compatible with both h1 and h2, even though dummyHandler could be assigned for both. In effect, despite the var, the assignment to h0 would force the right-hand operand to be converted to something which would not be assignment-compatible with things that the right hand side was convertible to. It's too bad that code relies upon testing delegates for equality (it's fundamental to the event model) since otherwise wrapping conversions would pose no problem, but since equality is important, wrapping conversions aren't safe.

Such behavior would not necessarily be unknown (consider, for example, that a const UInt32 which equals 4 is assignment-compatible with an Int32, but a var which is initialized with such a constant isn't), but it's still icky.
However, that's also the case for the F(int) and F(byte) functions, so we're already used to that.
If multiple methods may exist with a common signature, type inference can quickly go from being tricky to being intractable. Personally I'm not entirely convinced of the value of having type inference extend beyond cases that can be solved in polynomial time. If it takes a programmer an extra ten seconds to specify once something the compiler would otherwise have to spend an extra 100ms on every time the code is compiled, then it won't be long before that extra ten seconds would start paying off.
Jun 30, 2014 at 8:01 PM
supercat wrote:
How about var x=unchecked(u-i);? Try it if i is a const equal to 0, +1 or -1. Interesting?
I don't get your point.

supercat wrote:
I don't particularly like the rules for integers, but they're not arbitrary.
I never said that. The rules are not arbitrary, but the decision to add special rules for integers but not for functions could be considered arbitrary.

supercat wrote:
For the compiler to assume Action<T1,T2> given a method with two arguments would make something like the following fail: ...
I don't get your point. The fact that h0 can neither be assigned to h1 nor to h2 has nothing to do with the type inference we're discussing here. If it doesn't infer the type you want, don't let it infer the type. Same for integers: If you don't want an int, specify the type explicitly in what ever way you like best. I don't see how co- and contra-variance affects type inference when declaring variables.

supercat wrote:
If multiple methods may exist with a common signature, type inference can quickly go from being tricky to being intractable.
No, since I only want the default inference of Func<> and Action<>. We're not trying to solve the generic case here where arbitrary delegate types might be inferred.

supercat wrote:
If it takes a programmer an extra ten seconds to specify once ...
Obviously, you've never worked with F# or another language with strong type inference capabilities.

supercat wrote:
... something the compiler would otherwise have to spend an extra 100ms on every time the code is compiled, then it won't be long before that extra ten seconds would start paying off.
100ms? I'm sure you're exaggerating. But anyway, let me walk you through the type inference process and let's see if that can reasonably take a significant amount of time:
var f = (int x, bool y, MyObject z) => { var q = x + 1; if (y) return z.Do(q); else return z.Do(1); }
Well, we immediately see that the function type has three parameters of type int, bool and MyObject. That's as fast as resolving the types of any method declaration. Now what about the return type? Well, we have to find all exit points of the function, of which there are two in this case, both returning an expression, so we already know the inferred type must be Func<int, bool, MyObject, ?>. So only the return type is missing. Let's deduce the types of the expressions of the return statements. For that, we have to deduce the type of z.Do(q) and z.Do(1). We do that using the standard facilities, just like when the compiler checks whether the return types match for regular methods. Now we only have to check if the return types are compatible - but that is also already supported by the compiler. Consider:
void F<T>(Func<T> f){...}
F(() => { var q = 1; if (true) return z.Do(q); else return z.Do(1); });
So again, no work here that we don't normally do anyway. So why exactly should that be slow?
Jul 1, 2014 at 11:52 AM
What would I write if I need to infer an Expression<T> from a lambda? When I assign a lambda to a variable I almost always need an expression to compose it further, not a delegate. If you would add a discriminator to the syntax just for this case you might as well stick to plain old static methods. Almost the same amount of code at the call site, the compiler/jitter will inline them anyway, and no changes needed in the language.
static class As
{
  static Func<T, TResult> Func<T, TResult>(Func<T, TResult> it)
  {
    return it;
  }
  static Expression<Func<T, TResult>> Expr<T, TResult>(Expression<Func<T, TResult>> it)
  {
    return it;
  }
  // Add an overload for every delegate type you wish to use
}

var it1 = As.Func((int x) => x > 4);
var it2 = As.Expr((int x) => x > 4);
Jul 1, 2014 at 12:18 PM
Edited Jul 1, 2014 at 12:18 PM
supercat wrote:
void dummyHandler(object sender, EventArgs e) { }
var h0 = dummyHandler;
EventHandler<CatEventArgs> h1 = h0;
EventHandler<DogEventArgs> h2 = h0;
This seems like a weird example. The following already does not work:
Action<object, EventArgs> h0 = dummyHandler;
EventHandler<CatEventArgs> h1 = h0;
EventHandler<DogEventArgs> h2 = h0;
That didn't work before and is not going to work now. The proposed extension doesn't do anything good or bad to this scenario.
In which way does this mean being able to type var h0 = dummyHandler; and getting Action<object, EventArgs> inferred is not a productive addition?

Or are you saying that delegate handling should be structural and not nominal so that your example could work out of hand? I'd might like that, but that's not very likely to happen.

BachratyGergely: And you can type var i = 0; and not have to type var i = As.Int(0);. We're just asking for an extension of that syntactical convenience into some of the common delegate types.
Jul 1, 2014 at 12:24 PM
The problem with the As.Func method is that you cannot use them everywhere. In some places you have to use them, in other places you must not use them.

This proposal doesn't suggest anything different for expressions. Under this proposal, if you want an Expression-typed variable, you do the same thing as if you want to declare a variable of type "byte" and assign zero to it: be explicit. Use As.Expr, or a cast, or specify the variable type.

You're right, however, that this proposal reduces the similarity between lambdas and lambda expressions somewhat.
Jul 1, 2014 at 1:54 PM
Edited Jul 1, 2014 at 1:55 PM
JesperTreetop wrote:
BachratyGergely: And you can type var i = 0; and not have to type var i = As.Int(0);. We're just asking for an extension of that syntactical convenience into some of the common delegate types.
You are comparing a primitive type to a complex type expression. For numeric literals I think all of us prefer default int, and even if not, possible type names for numeric literals are much shorter, sometimes you can do away with a single-letter postfix. For lambdas there is no such thing as a short and compact type name, especially if you have to wrap an extra Expression<> around the delegate type. And while the type may be obvious the choice between anonymous method and expression tree is not. There should also be syntax to specify the latter and for that you would possibly need to add a new language construct. Not sure it's worth the effort.

rstarkov wrote:
The problem with the As.Func method is that you cannot use them everywhere. In some places you have to use them, in other places you must not use them.
I was strictly talking about the case when you need to write an overly long type declaration (or cast). What do you have in mind where this does not work and the current inference would not work either?

rstarkov wrote:
This proposal doesn't suggest anything different for expressions. Under this proposal, if you want an Expression-typed variable, you do the same thing as if you want to declare a variable of type "byte" and assign zero to it: be explicit.
I don't have much of a problem with inferring a Func<> for a particular matching signature if nothing better could be inferred. My issue is with inferring a delegate over an expression if the compiler could not decide. Which one is preferable may as well vary by project.
Jul 1, 2014 at 2:23 PM
Edited Jul 1, 2014 at 2:24 PM
BachratyGergely wrote:
while the type may be obvious the choice between anonymous method and expression tree is not.
I am fairly confident that if you did an analysis of people's real world source code you would find declarations of expressions to be rare compared to declarations of delegates. In fact, I can count on one hand the number of times I've needed to declare an expression. The language should make the common case easy- which is wanting inference to an Action or Func.
Jul 1, 2014 at 5:22 PM
I'd also say that working with delegates is more common than working with expressions. Only if you're writing IQueryable Linq providers or something similar you'll most likely be in the exact opposite situation, requiring expressions more often than delegates.

C#'s automatic creation of quotations (= Expression<> from a lambda) is nice syntactic sugar that is especially useful when querying databases using Linq. That's why it was invented in the first place. But it does have some other use cases. Therefore, one might consider adding a special quotation syntax element similar to F#'s <@@> to explicitly distinguish between delegate and expression lambdas. That would not only help with type inference of local variables, but it would also help to disambiguate invocations of overloaded methods, like so:
var f1 = (int x) => x > 4; // Inferred to be of type Func<int, bool>
// Using F#'s <@@> syntax for quotations:
var f2 = <@ (int x) => x > 4; @>; // inferred to be of type Expression<Func<int, bool>> 

void F(Func<int, bool> f) ...
void F(Expression<Func<int, bool>> f) ...
F(x => x > 4); // Invokes F(Func<int, bool> f)
F(<@ x => x > 4 @> // F(Expression<Func<int, bool>> f)
// Note that <@@> is only required for disambiguation; for reasons of conciseness and backwards compatibility implicit conversions of lambdas to expression must and should still be supported
And while we're at it, quotations should also support statements (the framework already does, the compiler does not). But both suggestions (explicit quotations and support for statements within quotations) are separate issues and feature requests, in my opinion.
Jul 1, 2014 at 7:52 PM
BachratyGergely wrote:
JesperTreetop wrote:
BachratyGergely: And you can type var i = 0; and not have to type var i = As.Int(0);. We're just asking for an extension of that syntactical convenience into some of the common delegate types.
You are comparing a primitive type to a complex type expression. For numeric literals I think all of us prefer default int, and even if not, possible type names for numeric literals are much shorter, sometimes you can do away with a single-letter postfix. For lambdas there is no such thing as a short and compact type name, especially if you have to wrap an extra Expression<> around the delegate type. And while the type may be obvious the choice between anonymous method and expression tree is not. There should also be syntax to specify the latter and for that you would possibly need to add a new language construct. Not sure it's worth the effort.
I'm not in favor of adding a new language construct for the ability to switch the inferred type. If you want to specify an inferred type, the ability to just actually write the type isn't going anywhere.

var x = () => 5; is illegal right now. If it could be made legal and produce a likely and usable type, I see that as a productive shortcut. It doesn't exactly uproot C#'s fundaments, but neither does ?. or readonly properties or indexers in the initialization expression. You're right that they don't have short and compact type names - that's why I'm in favor of the feature. var saves no characters over int, whereas it saves six over Func<int>. Declaring a variable containing a lambda is way more frequent than declaring a variable containing an expression tree, especially since most of the expression tree use is in libraries.
Jul 1, 2014 at 8:09 PM
Expandable wrote:
I don't get your point. The fact that h0 can neither be assigned to h1 nor to h2 has nothing to do with the type inference we're discussing here. If it doesn't infer the type you want, don't let it infer the type. Same for integers: If you don't want an int, specify the type explicitly in what ever way you like best. I don't see how co- and contra-variance affects type inference when declaring variables.
I grossly misunderstood your proposal; I thought for some reason you were expecting that the compiler would use downstream usage of the variable to infer the type [which would indeed be complicated and problematic]. Is the idea that the compiler should simply see if there is in scope a delegate named Func<...> or Action<...> with the proper arity and signature and if so, use it? If so, how much better is:
var f = (int a, int b) => ComputeSomething(a,b);
than would be
Func<int,int,int> f = (a,b) => ComputeSomething(a,b);
The argument types should be specified somewhere (lambda type inference may be able to work them out even if they're not satisfied, but the purpose of var is to define variables whose type is "obvious", and the solution to an NP-hard problem would hardly qualify.
var f = (int x, bool y, MyObject z) => { var q = x + 1; if (y) return z.Do(q); else return z.Do(1); }
In that scenario, the argument types are explicitly specified. Not much different from
var f = (Action<int,bool,MyObject) (x,y,z) => { var q = x + 1; if (y) return z.Do(q); else return z.Do(1); }
If the arguments weren't explicitly specified, there might be ways the compiler could figure out what they had to be, but that would again be going against the purpose of var; if one has to specify what the types of the arguments are, there's not a huge difference between specifying them in the delegate type versus in the parameter list. I could see some benefit to offering a syntax which would better associate argument types with their names, but I'm not sure the type Action<...> and Func<...> should be baked into the language itself. What would you think of something like:
var f = (Func<,,int>) (int x, int y) => x+y;
with the compiler filling in the omitted type arguments based upon the explicitly-given types on the right?