Your Abomination Is My Clever Hack

Tuesday, December 1, 2009

In the MvcContrib project there is an HTML helper for putting data into a <table>. The syntax looks like this:

<%= Html.Grid(item.Addresses)
            .Columns(column =>
                {
                    column.For(a => a.City);
                    column.For(a => a.Country);
                    column.For(a => a.Street);   
                })
            .Attributes(style => "width:100%")
            .Empty("No mailing address present!")
%>

I remember thinking this API was rather cool when Jeremy Skinner presented the syntax on his blog. The syntax became the topic of discussion in a recent Stack Overflow post - Abuse of C# lambda expressions or Syntax brilliance?

 I'm fascinated, yet at the same time repulsed, by a syntactic trick used in the Grid syntax:

.Attributes(style => "width:100%")

The syntactic trick is how the API uses the name of the lambda’s parameter as the name of the HTML attribute to set. You can set a bunch of the table attributes like this…

.Attributes(style       => "width:100%",
            id          => "grid",
            cellpadding => "0") 

…and it will render …

<table id="grid" style="width:100%" cellpadding="0">
   ....
</table>

Beautiful, don’t you think?

Bending Languages

bend I can appreciate the following thought process:

  1. How did they do that?
  2. Oh. My. Gosh.

I mean - nobody expects the name they pick for a parameter at compile time to be so important during runtime. The Stack Overflow post has a couple choice comments, including two from Eric Lippert:

This is horrid in so many ways

And …

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 don’t think one of the goals of C# is to “be like Lisp”, but I like the code, and it reminds me of a 1993 essay by Paul Graham titled “Programming Bottom-Up”.

Experienced Lisp programmers divide up their programs differently. As well as top-down design, they follow a principle which could be called bottom-up design-- changing the language to suit the problem. In Lisp, you don't just write your program down toward the language, you also build the language up toward your program. As you're writing a program you may think "I wish Lisp had such-and-such an operator." So you go and write it.

The project I’ve worked on for the last part of the year was a huge technical challenge. I bent the hell out of C# to try and make something that was easy to read and maintain. Looking back now, I can see a lot of mistakes and plenty of room for improvement, but I also believe if the project didn’t use some tricks with extension methods and Expression<T>, we’d be facing 3x the amount of code and 3x the complexity.

Since that time I’ve become a big believer in bending languages. You have to look past the tricky implementation details and see the bigger problem being solved.


Comments
gravatar Jason Snelders Wednesday, December 2, 2009
I love clean, simple and clever code. But isn't "bending the hell" out of language just another type of complexity? Think also about the poor sod that's not half as smart as you who has to maintain the code 1 year/5 years/10 years down the track (though I don't know if that's likely to be the case in your particular situation).
Code that's hard to write now is always 10 times harder to maintain later.
gravatar scott Wednesday, December 2, 2009
@Jason - I agree it is a type of complexity. In the project I've been working on I think about it a lot and it sometimes keeps me awake at night. I still think it's better than the alternative (one brute force approach I spiked had massive amounts of cyclomatic complexity), but sometimes I wonder if I used the right language for that particular job.
Duncan Mak Wednesday, December 2, 2009
Why use all that expensive reflection machinery when you could just use the collection literals? Is it to avoid typing "quotes" around your keys?

J Wednesday, December 2, 2009
Is this really seen as an abomination in the C# community?

My argument for why this is good seems pretty simple:

1) The code that does this isn't *that* hard to understand

2) All code that uses it becomes much more readable

3) The total of all code in #2 is going to be modified more often by several orders of magnitude than the code in #1
gravatar Jeremy Skinner Wednesday, December 2, 2009
It's a hack for sure.

I added support for this to the MvcContrib grid because I really dislike the use of anonymous types as dictionaries. I felt that this produced a cleaner result, but it's definitely language abuse :)

One thing to point out is that this is completely optional. All methods that take a 'lambda hash' also take an IDictionary as a separate overload.

@Duncan Mak,
This approach really isn't very expensive at all (see blog.bittercoder.com) and if you don't like it, you can always use a Dictionary + collection initializer. The idea wasn't to avoid quotes but to just make something that's a bit shorter to type.

Jeremy
gravatar Tom Janssens Wednesday, December 2, 2009
FYI : I'm personally a big fan of clever hacks. I published a response to your blog entry as well : www.corebvba.be/...
gravatar sean Wednesday, December 2, 2009
I did something similar for an ado.net wrapper that I released on CodePlex (called SqlSiphon if you care to search, I won't spam the link directly).

It's a series of methods in a class that is meant to be subclassed to make a data access layer for a project. You add methods to this subclass that exactly match the names of the stored procedures in your database. The code then walks the stack trace looking for your method (tagged by an attribute) and uses the names of the method and its parameters to construct the SqlCommand object needed to execute the stored procedure. It does some more shenanigans to process the results, but maybe you get the idea. Like I said, if you're really interested to see the tomfoolery, you can find it on CodePlex.

LINQ and lambdas are hugely important for it, the C# 2.0 version (necessary for some projects here at work) is easily twice as long.
gravatar Jon Fuller Wednesday, December 2, 2009
Yikes! That just blew my mind. I love it. I had somehow never considered using the name of the func argument before.

I love the Paul Graham quote, and while LISP'ers can do basically anything they want with their language, we can still raise the bar in C# with things like this.

My take: Beautiful; not a hack.
gravatar Bob Grommes Wednesday, December 2, 2009
This sort of thing can be evil in the same way that C++ macros are often evil. I don't want to see C# become a language where understanding an arbitrary code base involves understanding how the language was bent by those particular developers. Not unless there's a mighty damn good reason for it, beyond "wow, Dude, this is COOL!"
gravatar Matt Briggs Wednesday, December 2, 2009
That actually looks very close to rails code. They use hash literals as a sort of poor mans named parameter syntax. So something like generating a link is done with

<%= link_to 'Link Text', @model_object, :style => 'width: 100%', :id => 'model_link' %>

which would render as

Link Text

I think it is an elegant syntax, my only problem is that it is not exactly a conventional way of doing things in the C# world, to the point where it would not even be usable unless the person read the documentation before using it.

I think it is a beautiful way to push the language into interesting places. What I am saying is that it completely violates the principal of least astonishment (en.wikipedia.org/...), which is something you should strive for in public code.
gravatar Jeremy Skinner Wednesday, December 2, 2009
I've tried to address some of the issues raised with this approach over on my blog: www.jeremyskinner.co.uk/...
gravatar Alex Henderson (bittercoder) Thursday, December 3, 2009
The idea of hash-literal equivalents in C# has been around for ages - I first raised it on my blog back in 2007 (which is why I'm surprised it's sparking such a discussion when it's almost 3 years later ;o)

I recall taking it a bit further with nesting as per this post

Personally I feel these kinds of hacks are an indicator of a missing language feature, rather then something elegant (much like design patterns can be).

In this case I would much rather see language level support for symbols / more succinct dictionary initializer syntax. Or even better, support for AST macros / compiler pipeline extensions, like the often used "@symbol" => string transformation you see in many Boo-based DSL's.
Nadav Popplewell Thursday, December 3, 2009
This is just as much of a hack as using Anonymous Types as hashes in Asp.Net MVC:

When I first saw that in the MVC examples I thought:
It's a nice hack but I can't believe they are actually going to use this as the recommended way to pass parameters...

gravatar Jarin Udom Thursday, December 3, 2009
I gotta say as a Rails developer it's really weird to see people flipping out so hard about this.

"I mean - nobody expects the name they pick for a parameter at compile time to be so important during runtime."

This is a fundamental part of Rails development, and part of the reason it's so quick to write Rails apps.
gravatar Jason Thursday, December 3, 2009
Great post, I have to go with avoiding this as the anonymous type object has been working great for me in MVC.

This does seem to go against the purpose of lambdas and adds unnecessary complexity.

Also - I saw that "bend" building in tokyo, and took my own picture! haha nice coincidence, especially as it was sort of hidden on a side street!

picasaweb.google.com/.../Japan02
gravatar Demis Bellot Thursday, December 3, 2009
IMHO the holy grail of code quality is code that just lets you express your intent without the unnecessary noise of the language constructs (or work around hacks because of the lack of language features).

This is effectively the same goal of DSL's.

As long as it remains intuitive to read then I have no problems with API's that 'bends the language'.
Corey Thursday, December 3, 2009
I like the clever bending of the language, just not for this.

Personally, I prefer to look at html when in the view, not something designed to hide it.

If I wanted to hide my html I would have stuck with web forms.
gravatar Justin Chase Thursday, December 3, 2009
The part here that's not beautiful is the C# itself. This would be a much more beautiful in boo or a DSL.
gravatar Justin Chase Thursday, December 3, 2009
Also someone commented earlier:
"Why use all that expensive reflection machinery..."

I just wanted to point out that this probably doesn't use reflection at all. In C# when you write a lambda expression it will be compiled into 1 of 2 things, either it will be compiled into an delegate if the parameter type it is used as is a delegate (for example Func<T>) or it will be compiled as an expression, or Expression<Func<T>>. In the case of the expression, you can inspect the lambda, including parameters, without requiring reflection in the .NET sense (as in the System.Reflection API). I've seen the right hand side used this way before and it's pretty useful for things but not the left hand side.

So for people who are concerned about performance due to reflection costs can be relieved a little bit. Not that its free to look up the parameter name but it's extremely trivial (and cache-able if you're really worried).
gravatar Evan Bosscher Thursday, December 3, 2009
I've always tried to avoid clever hacks. I saw this quote once and love it:

The basic rule of thumb is that code is twice as hard to maintain as it is to write. If you're writing code you consider clever you are, by definition, not qualified to maintain it.

That being said, I also don't see all language bending to fall into that category. This particular piece of code is elegant and simple to understand (and I don't think lambas are easy to understand, lol)

Toss a couple of comments in there as to why you did it so that you'll never forget and run with it. :)
misteraidan Thursday, December 3, 2009
The cat is out of the bag. They're going to take it and run with it. There is no going back. We've past the final threshold. Opinions are obsolete.
gravatar Martinho Fernandes Monday, December 7, 2009
@Jarin: This is *not* the same as in Rails. While in Rails the :style part is a Symbol used as a key in the hash, here the style part is the name to an argument on a lambda. The equivalent Ruby would be:

attributes do |style| "width:100%"
gravatar Benjam&#237;n Eidelman Thursday, January 28, 2010
Hi, (i asked at the Jeremy's post, but aparently the discussion moved here)

I’ve been trying to specify the “class” attribute of the , but I can’t use the hash syntax:

.Attributes(class => “myCssClass”)

because “class” is a C# reserved word, is there any way to do this? (without having to create an IDictionary)

Thanks!
gravatar Scott Allen Thursday, January 28, 2010
@Benjamin: Use the @ symbol, as in @class => "myCssClass" should work.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!