The question that is the heading of your question is "why type inference doesn't work in this code?" Let just the code in question. The script is at the heart of:
class Bar { } interface I { int Foo(Bar bar); } class C { public static R M<A, R>(A a, Func<I, Func<A, R>> f) { return default(R); } }
Call site
CM(new Bar(), s => s.Foo);
We must define two facts: what is A and R ? What information do we need to continue? For new Bar() corresponds to A , and s=>s.Foo corresponds to Func<I, Func<A, R>> .
It is clear that we can determine that A must be a Bar from this first fact. And, obviously, we can determine that s must be I So, now we know that (I s)=>s.Foo corresponds to Func<I, Func<Bar, R>> .
Now the question is: can we conclude that R is int by doing overload resolution on s.Foo in the lambda body?
Unfortunately, the answer is no. You and I can conclude, but the compiler does not. When we developed the type inference algorithm, we considered the possibility of adding this type of "multi-level" output of the lambda / delegate / method, but decided that this was too high a cost in favor of what it could bring.
Sorry, you're out of luck here; in general, conclusions that require "digging" more than one level of functional abstraction are not implemented in a method such as the C # method.
Why does this work when using extension methods?
Because the extension method does not have more than one level of functional abstraction. Case extension method:
class C { public static R M<A, R>(I i, A a, Func<A, R> f) { ... } }
with call site
I i = whatever; CM(i, new Bar(), i.Foo);
Now what is our information? We find that A Bar , as before. Now we have to determine that R knows that i.Foo maps to Func<Bar, R> . This is a simple overload resolution problem; we pretend there was an i.Foo(Bar) call and allow overload work. Overload resolution is returned and says i.Foo(Bar) returns int , so R is int .
Note that this type of output - involving a group of methods - should have been added in C # 3, but I messed up and we did not do it on time. In the end, we added this output to C # 4.
Also note that in order to successfully complete such an output, all parameter types must be output . We should only output the return type, because in order to know the return type, we must be able to do overload resolution, and to perform overload resolution we need to know all types of parameters. We do not make any nonsense, like "oh, the group of methods has only one method, so let it skip overload resolution and just let this method automatically win."