Lambda Abuse: The MvcContrib Hash

There's recently been a lot of discussion on this post on StackOverflow about the use of lambda expressions to build dictionaries as part of the MvcContrib grid.

The Grid API allows you to define HTML attributes for a table by chaining a call to the 'Attributes' method as part of a grid definition:

<%= Html.Grid(Model.People).Columns(column => {
     		column.For(x => x.Id);
     		column.For(x => x.Name);
     		column.For(x => x.DateOfBirth);
.Attributes(style => "width:100%", cellpadding => 0)

This makes use of the MvcContrib.Hash class to construct an IDictionary from lambda expressions. So the following Hash definiton...:

var hash = new Hash(foo => "bar", baz => "blah"); the equivalent of:

var hash = new Dictionary<string, object> 
   { "foo", "bar" },
   { "baz", "blah" }

Internally, this is achieved by using a technique I first saw on Alex Henderson's blog. By taking an array of delegates, it is possible to capture the compile-time variable names used by the lambda expressions and use them as keys for the dictionary.

The responses to this approach have been quite polarized. There has been lots of positive feedback on twitter...

"@JeremySkinner I like it, no problem for me, infact I'll apply this technique where ever I can from now one forward :)" (from Mark Nijhof)
"@JeremySkinner the lambda abuse is awesome, very ruby symbols like, and pushing the language envelope." (from Adam Tybor)

...and also in the blogosphere (eg here and here) as well as on the StackOverflow post.

There has also been a lot of negative feedback:

"I hardly ever came across this kind of usage. I think it's inappropriate"
"I find that odd not so much because of the name, but because the lambda is unnecessary; it could use an anonymous-type and be more flexible"
"This is horrible on more than one level. And no, this is nothing like Ruby its an abuse of C# and .Net. "
"It's counter-intuitive, there is no way of just looking at the code to figure out what it does."

Interestingly, some of the most negative comments were from Eric Lippert on the C# compiler team:

"This is horrid in so many ways."
"I just asked Anders (and the rest of the design team) what they thought. Let's just say the results would not be printable in a family-friendly newspaper"

I thought Eric's response was possibly a little harsh, but wanted to try and address and explain some of the issues here:

Issue #1: This is not interop friendly!

One of the issues raised was that this is not interop friendly. If, for example, you wanted to build a grid using F# (or possibly another CLR language) then this approach would not work as it is dependent on the C# compiler. This is a good point and is very true.

The thing I want to point out here is that the syntax is completely optional. This particular overload for the 'Attributes' method on the grid is an extension method for convenience when using C#. The main implementation for this method takes an IDictionary. If you don't like the abusive-lambda approach (or you can't use it from another language) then you can use the 'regular' overload instead:

.Attributes(new Dictionary<string, object> { { "style", "width:100%" }, { "cellpadding", 0 } })

The abusive-lambdas are merely a preference - I happen to like it as it saves me some keystrokes. If you don't like it, don't use it.

Issue #2: This is unintuitive and not intellisense-friendly

From a readability perspective, I do not agree that this is unintuitive. If you saw "Attributes(style => "width:100%", cellpadding => 0)" in a grid definition then I think it's pretty obvious what's going on. It even looks like HTML attributes (kinda).

From an Intellisense perspective, I completely agree. If you see the following method signature, it is not immediately obvious what's going on:

public static IGridWithOptions<T> Attributes<T>(this IGridWithOptions<T> grid, params Func<object, object>[] hash)

But hold on a second...let's compare this to the approach that the ASP.NET MVC Framework uses for all its built-in HTML helpers:

public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes)

Notice that "object htmlAttributes" at the end? That allows you to specify an anonymous type containing HTML attributes:

<%= Html.TextBox("foo", "bar", new{ style = "width:100%", @class="foo" })

This is just as opaque as using abusive lambdas. In fact, I'd aruge that it's *more* opaque because the htmlAttributes parameter is defined as type object. This means you could accidentally pass *anything* in this parameter (eg a string) and the compiler would not complain. I have done this on many occasions and I find it very frustrating. At least with a parameter of type Func<object, object>[] you can't accidentally pass in a string. (Much like the MvcContrib approach, ASP.NET MVC's helpers also have overloads that take an IDictionary if you prefer).

Issue #3: You can achieve the same result with an anonymous type:

I don't believe that the Hash is really any worse than the anonymous type. In fact, in some ways I think the use of an anonymous type is worse (see my point above regarding opacity)

Issue #4: Performance implications!

There seems to be some misunderstanding around the use of the lambda syntax. On the one hand you can use a lambda to define an expression tree which can then be parsed (this is how LINQ to SQL works - the expression tree is then converted to SQL). These expressions can then be compiled to a delegate and invoked which is an expensive operation. However, lambdas can also be used for anonymous methods. This is essentially syntactic sugar over a standard .NET delegate and is an improvement over C# 2's anonymous delegate syntax.

The MvcContrib uses the latter approach to do its work. This way, there is no overhead for compiling the expressions. In fact, there is very little overhead versus using the standard Dictionary.Add method. Alex Henderson talks more about the different approaches for doing this on his blog, but these were the results of his benchmark for 10000 calls:

  • Using Dictionary.Add: 10.0144ms
  • Using lambdas with expression compilation: 9713.968ms (240.3456ms with constants)
  • Using lambdas as delegates: 30.0432ms

Issue #5: Why not use method chaining?

One suggestion was to chain multiple calls to 'Attribute':

.Attribute("style", "width:100%").Attribute("cellpadding", "0")

This definitely works, but the whole reason why I decided to use the Hash was because I want to mimimize the amount of typing (my fingers tire easily) and making multiple calls to 'Attribute' is rather verbose.

And finally...

In the end, this is really just down to personal preference:

  • I do not like C#'s dictionary initializers - I think they're too verbose
  • I do not like the use of anonymous types as dictionaries as they require you to take parameters of type "object"

The Abusive Lambda Pattern (I think that's what I'll call this) is just something I happen to like, but if you don't like it then you certainly don't have to use it.

Perhaps Eric and the C# team would like to give us a more succinct collection initializer for Dictionaries in C#5? :)

Written on December 2, 2009