C# 3.0 Lambdas and Type Inference
Also see: Merry Christmas Indeed!
Daniel Cazzulino recently wrote a blog entry whose main focus was on building pipelines using iterators in C#. Towards the end he showed a slightly irritating problem in C# 3.0. He wanted to write this:
var transformer = x => new { Original = x, Normalized = x.ToLower() };
However, the C# compiler complains because it doesn’t have enough information to infer the type of the transformer variable. The problem it reports is “Cannot assign lambda expression to an implicitly-typed local variable”.
Daniel doesn’t present a working solution to this particular problem – he ends up structuring his program differently to avoid the issue entirely. But in his discussion of this problem, he proposes something that he describes as ugly, and which, as he points out, doesn’t work anyway:
Func<string, {string Original, string Normalized}> transformer = x => new { Original = x, Normalized = x.ToLower() };
This is a direct approach to the problem described in the compiler error message. Can’t assign the expression to an implicitly-typed variable? OK, let’s make the variable explicitly typed. Unfortunately, you can’t specify the type because the expression involves an anonymous type. And that’s the thing about anonymous types: they don’t have names.
Daniel has made up a plausible syntax for denoting unnamed types: the first part in {} shows a possible solution. But that was just a hypothetical suggestion on his part – in fact C# 3.0 doesn’t provide a way to denote anonymous types.
Also see: SIGPLAN Workshop on Undergraduate Programming Language Curriculum
Also see: Generating WPF Content with LINQ
Also see: Interested in Artificial Intelligence? What about Wiki’s? Well, now you can have both.
Also see: Load(AssemblyName)
Also see: Single source code base for Silverlight and WPF solutions
Also see: Reporting Services administration changes in Katmai (v.Next)
Also see: Blogging and Newspapers, a Lesson in How Not to Brand and Market
Also see: ASP.NET MVC in CodePlex and Extensible Unit Testing
Also see: From C# to Java: Part 3
Also see: Quick attempt at a validating roman numeral parser… Lots of gotchas.
Also see: LoadFile vs. LoadFrom
Also see: Spring Web Flow features and feedback request
Also see: Finally, the Killer App
Also see: Aggregators rock!
Also see: Never keep your emotions bottled up
Also see: I’ve finally settled into my new position on the Internet Explorer team…
Also see: Resizing a Form has always been a pain in the rectum…
Also see: I’ve finally settled into my new position on the Internet Explorer team…
Also see: Microformats are like RFID tags for the Web
Also see: Playing Multiple Simultaneous Sounds in WPF
Also see: Introducing Microsoft Tagspace
Also see: Blogging and Newspapers, a Lesson in How Not to Brand and Market
Also see: Doing the Deal and Dishing the Dirt
Also see: Updated Finalization and Hosting
Also see: REST2SQL in a Jiffy, with Tagspace for Spice
Also see: On the Perils of Wikipedia
But there is a solution. We carry on using an implicitly-typed variable, and fix the expression instead. The error says we can’t use a lambda expression. OK – let’s use something else. And as you’ll see, we don’t even have to get rid of the lambda. We can just wrap it in something.
Why Can’t I Use a Lambda?
To decide what to use in place of the raw lambda expression, we need to understand why C# isn’t letting us use a lambda here. The problem is that lambda expressions don’t have an intrinsic type. They’re more adaptable than that – a given lambda expression could mean several different things. The most straightforward demonstration of this is that the exact same expression could evaluate to either a delegate or an expression tree.
The compiler uses the context to determine the type. If you’re trying to assign or pass the lambda to something expecting a delegate, it’ll turn into a delegate. But if the target expects an expression tree, you’ll get one of those instead.
In short, a lambda’s expression’s evaluated type is determined by the context in which it’s used.
This gives us a problem with var: var says that its type is determined by the expression with which it is initialized. So when var meets a lambda, it’s like the two are stood by a doorway, each politely saying “No, after you”. (Apologies to my international readers – I suspect that might be a slightly over-British analogy…)
Something needs to decide the type. But the obvious approaches (ordinary explicitly-typed variables, or casts) end up hitting the problem Daniel hit: you can’t denote an anonymous type, so how do you specify the type? But it turns out that you don’t need to specify the type completely. All you really need to do is provide just enough information to let the C# compiler proceed.
Providing Just Enough Type Information
We can narrow down the C# compiler’s options without fully specifying the type. The trick is to go via a function call. Consider this apparently pointless function:
public static Func<T, TResult> Funcify<T, TResult>(Func<T, TResult> f) { return f; }
What use would that be, you may wonder? It takes any single-parameter function and returns that function without doing anything with or to it!
However, this provides the C# compiler with a little more information. If I pass a lambda as a parameter to Funcify, I’ve closed down an option: the C# compiler knows that whatever it produces needs to be a Func – it no longer has the option to produce an expression tree. This might be enough to disambiguate matters. Unfortunately, it’s not quite enough to fix our example, as we’ll see when we try it:
var transformer = Funcify( x => new { Original = x, Normalized = x.ToLower() });
This gets rid of the previous error, but replaces it with a new one:
“The type arguments for method ‘Funcit.Program.Funcify<T,TResult>(System.Func<T,TResult>)’ cannot be inferred from the usage. Try specifying the type arguments explicitly.”
But that’s progress. Honest! The compiler is no longer telling us that we can’t do this with var. It’s now telling us that it doesn’t have quite enough information.
Also see: LINQ - The Uber FindControl
Also see: My Presidential Endorsement:
Also see: Microformats are like RFID tags for the Web
Also see: A first stab at BaseN encoding with a focus on general alphabet encoding.
Also see: Reporting Services administration changes in Katmai (v.Next)
Also see: Never keep your emotions bottled up
Also see: C# 3.0 Lambdas and Type Inference
Also see: Dare Obasanjo on C# Anonymous Types
Also see: Claimspace: Against a Well-designed Reputation System
Also see: Sliced Bananas On Opaque Data
Also see: The PDC and Application Compatibility, but still no Hosting
Also see: A web site is not an RSS feed…nor the reverse.
Also see: Merry Christmas Indeed!
Also see: LINQ - The Uber FindControl
Also see: The PDC and Application Compatibility, but still no Hosting
Also see: Reporting Services administration changes in Katmai (v.Next)
Also see: Microformats are like RFID tags for the Web
Also see: Quaker votes
The problem here is that it doesn’t know what ‘x’ is. And how could it? Any type with a ToLower method would do. The intent here is for it to be a string, so we just need to express that:
var transformer = Funcify( (string x) => new { Original = x, Normalized = x.ToLower() });
And now we’re good. The compiler is happy, because we gave just enough information to pin things down and it was able to infer the rest.
Mumble Types
I’ve seen in some C# team members’ blogs a suggestion for something they call ‘mumble types’. If I’ve understood them correctly, the idea is that you can provide a type specification, but you get to ‘mumble’ (i.e. be unclear) about parts of the type specification. This would give us an alternative solution:
Func<string, ?> transformer = x => new { Original = x, Normalized = x.ToLower() };
This doesn’t work today, but I would prefer this solution if it were available. It does exactly what I did – it lets me specify just enough to let the compiler’s type inference finish the job – but it doesn’t require the questionable hack of my Funcify function. And I think it’s clearer – with my example, it’s not particularly obvious that I was trying to fix some parts of the type specification while leaving other parts to be inferred. But with this ‘mumble type’ approach, it’s a lot more obvious that I’m saying ‘I want it to be a Func, and I want the input to be a string, but I want the compiler to infer the Func’s return type’. Also, this approach avoids the clutter of specifying the string type in the lambda parameter list itself, so I find it somewhat more aesthetically pleasing.
Improve performance of customer support with Jerry Messenger. Live Chat Support to your website visitors.
Also see: Finally, the Killer App
Also see: TransparentProxy
Also see: Why I hate Radio
Also see: Programmers At Work
Also see: Music and Movies - Give Away the Soundtrack
Also see: Interested in Artificial Intelligence? What about Wiki’s? Well, now you can have both.
Also see: Microformats are like RFID tags for the Web
Also see: Exception Handling in Running a Business
Also see: Why I hate Radio
The use of the ‘?’ is based on what I’ve seen in C# team members’ blogs in the past year or two. However, here’s a different syntax to consider:
Func<string, var> transformer = x => new { Original = x, Normalized = x.ToLower() };
We already use the keyword var to say ‘the compiler should deduce this type’ for variable declarations. So I think it would be consistent to use the same keyword to mean the same thing in partially specified generic types too. That said, the ‘?’ has the benefit of standing out. And it seems reminiscent of Haskell’s use of ‘_’ to mean ‘whatever’. So I could see either approach, but I think I slightly prefer the idea of using var.
Gratuitous Plug for Training in London
And finally, some advertising. I’m teaching a couple of training courses for Pluralsight in London soon. I’ll be running the Silverlight course that Fritz Onion and I co-author at Old Street from 31st March to 3rd April. And I’ll be teaching our WPF course at the same location the following week – from 7th to 10th April.
http://www.interact-sw.co.uk/iangblog/2008/03/17/lambda-inference
