More On The Death of If-Else

Sunday, December 6, 2009 by K. Scott Allen
24 comments

Aaron Feng posted recently on “The death of if-else, if, and else”. In the post Aaron rewrote some JavaScript conditional checks using a dispatch table type approach.

Following along with Aaron’s post using C#, we’d start with a list of Channel objects:

var channels = new List<Channel>
{
    new Channel { Number = 2,
                  Station = "NBC",
                  ShowTitle = "Saturday Night Live",
                  Genre = "comedy",
                  Repeat = true },
    new Channel { Number = 3,
                  Station = "ESPN",
                  ShowTitle = "College Football",
                  Genre = "football",
                  Repeat = false}
    // ...
};

Then we have the logic for deciding which channels to record:

public void Surf(IEnumerable<Channel> channels)
{
    foreach (var channel in channels)
    {
        if (channel.Genre == "football")
        {
            Record(channel);
        }
        else if (channel.Genre == "comedy" && 
                 !channel.Repeat)
        {
            Record(channel);
        }
        else if (channel.Genre == "crime" &&
                channel.ShowTitle != "Cops!")
        {
            Record(channel);
        }
    }
}

Rewriting the code using Aaron’s final approach would look like the following:

public void Surf2(IEnumerable<Channel> channels)
{
    var dispatch = new Dictionary<string, Action<Channel>>
    {
        { "football", c => Record(c) },
        { "comedy",   c => {if(!c.Repeat) Record(c);}},
        { "crime",    c => {if(c.ShowTitle != "Cops!") Record(c);}}
    };

    foreach (var channel in channels)
    {
        dispatch[channel.Genre](channel);
    }
}

Personally, I feel Aaron’s dispatch table is not a big improvement over the previous “if else” version. The actions in the dispatch table are too busy. I think a cleaner approach is to just extract the rules into a data structure – essentially build a collections of predicates to evaluate. Given a channel, the data structure can tell me if the channel should be recorded.

public void Surf3(IEnumerable<Channel> channels)
{
    var recordingRules = new Func<Channel, bool>[]
    {
        c => c.Genre == "football",
        c => c.Genre == "comedy" && !c.Repeat,
        c => c.Genre == "crime" && c.ShowTitle != "Cops!"
    };

    foreach (var channel in channels)
    {
        if(recordingRules.Any(rule => rule(channel) == true))
        {
            Record(channel);
        }
    }
}

Not quite as flexible, but for this specific example its easier to read and maintain.

What do you think? Is that better or worse?

Your Abomination Is My Clever Hack

Tuesday, December 1, 2009 by K. Scott Allen
24 comments

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.

by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!