Lambda Expressions vs. Anonymous Methods, Part Two
We interrupt the discussion of how the difference between lambda expression and anonymous method convertibility leads to potential performance problems in the compiler to answer a user question.
Within hours of yesterday's post readers Raymond Chen and Marc Brooks both asked me the same question. How do lambdas and anonymous methods interact with implicitly typed local variables?
Recall that C# 3.0 will support implicitly typed local variables. This:
var d = new Dictionary<string, List<int>>();
is a syntactic sugar for
Dictionary<string, List<int>> d = new Dictionary<string, List<int>>();
Again, I wish to emphasize that C# 3.0 is still statically typed, honest! The var keyword does not have the semantics of the JScript var or VBScript's Variant or any such thing. It simply means "the variable is the type of the right side of the declaration". We are committed to keeping C# 3.0 a statically typed language.
You will note that a key requirement there is that the right hand side actually has a type. Furthermore, it cannot be the null type or the void type, obviously. So then what should this do?
var f = i=>F(i);
Clearly this cannot be legal; since as I discussed yesterday the type of the formal parameter is determined from the target type, and we are trying to determine the target type, we have a chicken-and-egg problem where there is not enough information to determine the type.
What about this?
var f = (int i)=>i;
Here we know the type of the parameter, we can infer the type of the return easily enough, it's the same type as the parameter! We're set, right?
Not so fast.
Consider the following type declarations:
public delegate int D1(int i);
public delegate int D2(int i);
public delegate int D3<A>(A a);
public delegate R D4<A, R>(A a);
If the var were replaced by D1, D2, D3<int>, D4<int, int>, or for that matter, D4<int, double> or D4<int, object>, we'd have a legal program. So what is the type of the right hand side? How can we choose which of these is the best?
We can't. Delegate types are not even structurally equivalent; you can't even assign a variable of type D1 to a variable of type D2. We would always potentially choose wrong.
What this tells us is that it is more than just that the formal parameter types of an implicitly typed lambda expression flow from the target type. The type of the entire lambda expression flows from the target type, and therefore, lambda expressions themselves must have no type.
And in fact the C# 2.0 specification calls this out. Method group expressions and anonymous method expressions are typeless expressions in C# 2.0, and lambda expressions join them in C# 3.0. Therefore it is illegal for them to appear "naked" on the right hand side of an implicit declaration.
This is rather unfortunate because it makes it difficult to declare a variable of type "delegate which takes or returns an anonymous type". The whole point of the var feature in the first place is to make anonymous types work, and yet we seem to have a hole here. As it turns out, it is possible! Next time I'll describe a clever trick whereby we can use method type inference to make variable type inference work correctly on lambdas which take anonymous types.
Comments
Anonymous
January 11, 2007
Welcome to the eighteenth issue of Community Convergence. I'm Charlie Calvert, the C# Community PM, andAnonymous
January 12, 2007
Last time I said that I would describe a sneaky trick whereby you can get variable type inference outAnonymous
January 12, 2007
The comment has been removedAnonymous
February 05, 2007
Why is there such a strong insistence in C# on not identifying structurally equivalent delegate types, especially since the introduction of anonymous types makes a completely nominative type system somewhat impossible? Is it just because you don't want to have massive backwards compatibility problems? If you identified structurally equivalent delegate types, then you could allow anonymous delegate types, as C# requires that all structurally equivalent anonymous types in the same method are identified. This would be a pretty reasonable solution to your conundrum, but perhaps I am missing a subtlety here due to a lack of deep experience with the C# type system. And barring such a jarring change to existing C# features, couldn't you just introduce anonymous delegate types regardless, with the usual anonymous type semantics of divining that the programmer intended all structurally equivalent anonymous delegate types in the same method to be identified?Anonymous
June 12, 2008
NOTE: If you haven't read the first post in this series, I would encourage you do to that first , orAnonymous
February 11, 2009
NOTE: If you haven't read the first post in this series, I would encourage you do to that firstAnonymous
June 28, 2010
Huh. I guess this is why Haskell doesn't have implicit conversions?