How Ruby Taught Me To DRY Up My Code With Lambda Blocks

I’ve been working in Ruby for my Albacore project over the last 6 or 8 months, and taking every chance I can find to learn how to really use the language effectively. One of the benefits I’m seeing in a dynamic language like Ruby is the ability to really DRY up your code through it’s dynamic/duck type system, and through metaprogramming.

I’ve noticed in my ruby code that I tend to see repeated patterns of implementation in a different light. Rather than seeing the things that make each repetition of the pattern different, I tend to see the things that make each repetition of the pattern the same. I notice the same structure used with different variable name, the same method calls used with different parameters, and context specific method calls as the outliers that made me duplicate the code in the first place. When I see these patterns, my mind begins to run down the path of “this code is duplicated… how can I eliminate that duplication?” Whereas in C#, I almost immediately see the differences as “these are different calls based on the context and I can’t eliminate this repeated pattern of code because of the unique calls each has to make.”

I’m not sure why my mind has been operating this way with C#, but I know that is has been doing this for a very long time. I’ve often written the same pattern of code 6 or 8 times, or more in some cases – especially when it comes to UI code and event handlers from UI controls. I wrote a prime example of repeated patterns in C# just today, on a UI that has 4 ComboBox controls on it. Each combobox has a SelectedIndexChanged event handler that gets the selected value and pushes it to the presenter via a presenter method that is specific to the value being pushed. Here’s the code in all it’s glorious duplication:

   1: void cboTypes_SelectedIndexChanged(object sender, EventArgs e)

   2: {

   3:     TeardownComboBoxEvents();

   4:  

   5:     var lookup = cboTypes.SelectedItem as Lookup;

   6:     if (lookup != null)

   7:     {

   8:         _presenter.TypeSelected(lookup);

   9:     }

  10:  

  11:     SetupComboBoxEvents();

  12: }

  13:  

  14: void cboGroups_SelectedIndexChanged(object sender, EventArgs e)

  15: {

  16:     TeardownComboBoxEvents();

  17:  

  18:     var lookup = cboGroups.SelectedItem as Lookup;

  19:     if (lookup != null)

  20:     {

  21:         _presenter.GroupSelected(lookup);

  22:     }

  23:  

  24:     SetupComboBoxEvents();

  25: }

  26:  

  27: void cboCategories_SelectedIndexChanged(object sender, EventArgs e)

  28: {

  29:     TeardownComboBoxEvents();

  30:  

  31:     var lookup = cboCategories.SelectedItem as Lookup;

  32:     if (lookup != null)

  33:     {

  34:         _presenter.CategorySelected(lookup);

  35:     }

  36:  

  37:     SetupComboBoxEvents();

  38: }

  39:  

  40: void cboCodes_SelectedIndexChanged(object sender, EventArgs e)

  41: {

  42:     TeardownComboBoxEvents();

  43:  

  44:     var lookup = cboCodes.SelectedItem as Lookup;

  45:     if (lookup != null)

  46:     {

  47:         _presenter.CodeSelected(lookup);

  48:     }

  49:  

  50:     SetupComboBoxEvents();

  51: }

When I wrote this code and looked back at it, I had my usual feeling of “well, these presenter calls are specific the the context of the combox being selected, so I can’t really do anything to eliminate this repeated pattern of code.” I even went so far as to think “man, if this were Ruby, I wouldn’t have any issue killing this repeated pattern.”  That’s when a little voice in the back of my head started shouting at me and I realized that I could eliminate the duplication in C# just as easily as I could in Ruby with the use of anonymous delegates.

 

Method Blocks And Anonymous Delegates

One of the techniques I often use in Ruby to help dry up repeated code is ruby’s method blocks – basically an anonymous delegate in C#. These two code samples are functionality equivalent…

Ruby Method Block With Named Parameter

   1: def my_method(&block)

   2:   name = "derick"

   3:   block.call(name) unless block.nil?

   4: end

   5:  

   6: my_method do |name|

   7:   puts "the name is: #{name}"

   8: end

C# Anonymous Delegate (Lambda) With Named Parameter

   1: public void MyMethod(Action<string> block)

   2: {

   3:   string name = "derick";

   4:   if (block != null)

   5:     block(name);

   6: }

   7:  

   8: MyMethod(name => {

   9:   Console.WriteLine("the name is: " + name);

  10: });

 

Eliminating This Repeated Pattern

After I finally decided to listen to that little voice shouting at me and use my tools to their full extent, I rewrote the event handlers into the following code, using an Action delegate and anonymous lambda block to execute the context specific presenter calls.

   1: private void LookupSelected(ComboBox comboBox, Action<Lookup> presenterCall)

   2: {

   3:     TeardownComboBoxEvents();

   4:  

   5:     var lookup = comboBox.SelectedItem as Lookup;

   6:     if (lookup != null)

   7:     {

   8:         presenterCall(lookup);

   9:     }

  10:  

  11:     SetupComboBoxEvents();

  12: }

  13:  

  14: void cboTypes_SelectedIndexChanged(object sender, EventArgs e)

  15: {

  16:     LookupSelected(cboTypes, l => _presenter.AssetTypeSelected(l));

  17: }

  18:  

  19: void cboGroups_SelectedIndexChanged(object sender, EventArgs e)

  20: {

  21:     LookupSelected(cboGroups, l => _presenter.GroupSelected(l));

  22: }

  23:  

  24: void cboCategories_SelectedIndexChanged(object sender, EventArgs e)

  25: {

  26:     LookupSelected(cboCategories, l => _presenter.CategorySelected(l));

  27: }

  28:  

  29: void cboGroups_SelectedIndexChanged(object sender, EventArgs e)

  30: {

  31:     LookupSelected(cboGroups, l => _presenter.GroupSelected(l));

  32: }

 

Lessons Learned

This certainly isn’t anything extraordinary, mind you. I’ve written methods with delegates and lambda blocks more often than I can remember. This code is not complex, it’s not difficult to write, it’s not difficult to read or understand. But that’s the beauty of it. It’s simple, elegant, and eliminates the repeated pattern that I was creating. There are probably some additional tweaks I could make, honestly, but I also want to keep in mind the readability and understandability of the code – not just how often a pattern is repeated.

The significance of this is not in the code that I wound up writing, but in how I came to that decision. My exposure to ruby and my predisposition to see repeated patterns of code in ruby as duplication that should be eliminated finally made a jump across the neuro-pathways of my brain into C# land. I was able to take a paradigm from a different language and different set of optimizations and capabilities, and redefine my own understanding of the current paradigms and capabilities of this situation. That kind of cross-breading and transfer of knowledge is critical to our ability to come up with new and creative solutions in situations where we believe we already have mastery.

Do yourself a favor – learn a new paradigm of development or whatever your job entails. You’ll never truly be able to say “use the right tool for the job” unless you actually know how to use the tools available, and you never know when the paradigms of one tool will cross the boundaries of your experience and begin to show you new solutions to existing problems.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in .NET, C#, Lambda Expressions, Model-View-Presenter, Pragmatism, Principles and Patterns, Ruby. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://jonkruger.com/blog Jon Kruger

    It’s amazing how much I (and the rest of the .NET community) have learned from Ruby on Rails. So many of us have been in this little .NET bubble for so long that we don’t even know how to think outside the box anymore. Ruby really forced me to think differently and opened my eyes to a lot of things.

  • Steve Calvert

    I’ve found that using JavaScript has helped improve my C# in similar ways. Looking at things from a functional programming perspective also helps to see where patterns take shape.

  • http://neilmosafi.blogspot.com Neil Mosafi

    You could have taken this a bit further. Like so:

    private EventHandler MakeHandler(ComboBox cb, Action presenterCall)
    {
    return (s, e) =>
    {
    TeardownComboBoxEvents();

    var lookup = cb.SelectedItem as Lookup;
    if (lookup != null)
    {
    presenterCall(lookup);
    }

    SetupComboBoxEvents();
    };
    }

    Then when you wire up your handler, do it as follows:

    cboGroups.SelectedIndexChanged += MakeHandler(cboGroups, l => _presenter.GroupSelected(l));

    What do you reckon?

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    @Neil,

    I like it! I usually have to wire up my events manually, anyways… that cuts down on the clutter significantly. :)

  • http://www.agilification.com Jeff Doolittle

    You can do something similar like this:

    private void ConfigureComboboxEvents()
    {
    cboTypes.SelectedIndexChanged += (sender, e) => LookupSelected(cboTypes, l => _presenter.AssetTypeSelected(l));
    cboGroups.SelectedIndexChanged += (sender, e) => LookupSelected(cboGroups, l => _presenter.GroupSelected(l));
    cboCategories.SelectedIndexChanged += (sender, e) => LookupSelected(cboCategories, l => _presenter.CategorySelected(l));
    }

    private void LookupSelected(ComboBox comboBox, Action presenterCall)
    {
    TeardownComboBoxEvents();

    var lookup = comboBox.SelectedItem as Lookup;

    if (lookup != null)
    {
    presenterCall(lookup);
    }

    SetupComboBoxEvents();
    }