9

When S and T are different, this works:

public static void Fun<S, T>(Func<S, T> func)
{

}

Fun((string s) => true); //compiles, T is inferred from return type.

But,

public static void Fun<T>(Func<T, T> func)
{

}

Fun(t => true); //can't infer type.

In the first example, since T is inferred from return type of lambda expression, can't T in the second example too be inferred? I suppose its doing it, but why is first T not known, when second T of Func<T, T> is known, after all T == T right? Or is there an order for types being inferred in case of Funcs?

nawfal
  • 70,104
  • 56
  • 326
  • 368
  • `S` is not inferred in the first example, so its not a question of difference. I agree that it looks as if inference should work in the second example. – Jodrell Feb 12 '13 at 16:40
  • @Jodrell sure `S` is not inferred, it cant, but `T` is, isnt it.. – nawfal Feb 12 '13 at 17:15

2 Answers2

13

It has nothing whatsoever to do with S and T being different. It has to do with you supplying the formal parameter type in the first case and not supplying it in the second case.

Method type inference does not attempt to infer the return type of a delegate from a lambda until the formal parameter types of the delegate are known.

In the second case you've given the compiler nothing with which to infer the formal parameter type T, and therefore the body of the lambda will not even be analyzed.

What do you mean by "formal parameter type"?

A formal parameter is a variable that takes on the value of an argument passed to a method, indexer, constructor, lambda or anonymous method. (Or, in the case of out and ref formal parameters, becomes an alias to a variable supplied by the caller.) Formal parameters are variables, and therefore have types.

The delegate delegate R Func<A, R>(A a); has formal parameter a with type A. You construct that with method type parameters to make Func<S, T>, so the formal parameter type of the delegate is now S. The task of type inference is to infer those types S and T.

In your first example you have a lambda with formal parameter s of type string. So type inference reasons that since this lambda argument corresponds to formal parameter func of method Fun, and the formal parameter type of func is Func<S, T> then the formal parameter type of s must correspond to S. Since you gave a formal parameter type for s, S is inferred to be string.

Once that inference is made then T can be inferred by analyzing the body of the lambda.

In your second case, there is no formal parameter type given for t. Since there is nothing else from which the type of t can be deduced, type inference gives up and abandons analyzing this lambda before looking at the body.

It just so happens that in your case the body could be analyzed independently of the formal parameter type of the lambda. That is a rare case and the type inference algorithm was not written to take advantage of it.

If this is the sort of type inference you want, consider using F# instead of C#. It has a far more advanced type inference algorithm, based on the Hindley-Milner algorithm.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • What is "formal parameter type"? I only provided `S` in first case isn't it? So here `S` is the formal parameter? So you mean input types right? – nawfal Feb 12 '13 at 17:11
  • 1
    @nawfal, I understand this from the answer, `Func`. – Jodrell Feb 12 '13 at 17:15
  • 1
    I'll update the answer. It's too complicated to explain in a comment. – Eric Lippert Feb 12 '13 at 17:16
  • 1
    @nawful - See section 7.5.2.12 of the C# spec. Inferred return type: "The inferred return type can only be determined for an anonymous function **where all parameter types are known**, either because they are explicitly given, provided through an anonymous function conversion, or inferred during type inference on an enclosing generic method invocation." – P.Brian.Mackey Feb 12 '13 at 17:20
  • 1
    http://en.wikipedia.org/wiki/Hindley%E2%80%93Milner now thats complicated. I found your explanation relatively straight forward. – Jodrell Feb 12 '13 at 17:35
  • 3
    @Jodrell: The notation for the HM rules looks terrifying until you learn to read all that Greek, and then it becomes actually quite straightforward. For example, `Γ⊢e:τ→σ Γ⊢f:τ / Γ⊢ef:σ` simply means "if you have evidence that `e` is a function from `T` to `S`, and you have evidence that `f` is an expression of type `T`, then you have sufficient evidence to deduce that the expression `e(f)` is going to be of type `S`. The notation is extremely concise compared to writing it out in English, but it really is not expressing anything very complicated. – Eric Lippert Feb 12 '13 at 17:57
  • 1
    Its interesting to note that if there happened to be no formal parameter at all, like in case of `Func`, then return type is inferred, for example `Method(() => true)` where definition of the method looks like `Method(Func func);`. – nawfal Dec 16 '13 at 14:47
5

Generic parameters for lambdas and other functions are determined by their parameter types, not their return type. It's exactly the same reason why you cannot do this:

T Foo<T>() { return default(T); }

string x = Foo(); // error

For the expression t => true, we clearly don't know what t could possibly be so the compiler cannot make any more decisions based on this alone.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272