Hacking C#’s Lambda Expressions Into Hash Rockets

c# loves RubyAs I move between C# and Ruby, I have found my brain’s internal syntax parser always needing to switch gears and repurpose its understanding of Fat Arrow, =>. In Ruby, it provides a visually salient means of expressing key => value pairing within a Hash. C# on the other hand uses it to indicate the opening of a lambda expression’s body block, x => x + y. Its notable that in other languages, such as Coffee Script, it has a similar meaning. In any case, the lines sometimes blur as I’m dreaming up new ways to make C# look and behave more like my favorite dynamic language.

In this post, I’m going to show you how to repurpose C#’s lambda expression syntax for creating key,value pairs.  My goal is be able create nestable enumerable graph structures with a syntax like this:

var rockets = __.Rocketize(
                               foo => "asdf",
                               bar => 42,
                               biz => new Business{ Name = "AMD" },
                               now => DateTime.Now,
                               fun => new Func(() => return new Awesome(source: "Joel Holder")),
                               sub => __.Rocketize(a => 'b',
                                                   c => 'd',
                                                   e => 'f'),
                               xml => File.ReadAllText(@"data.xml"),
                               web => new Uri("https://uberpwn.wordpress.com/"),
                               ___ => typeof(__),
                               tru => (2*2+3*3)/(5*5) == 1,
                               etc => "..."
                          );

First we need a few functions that leverage the Expression API to provide a means of taking in a series of lambda expressions. Internally, we will convert each expression’s AST into a named key and value of Func<> that  returns an optional state object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;

namespace HashRocket
{
    public class __
    {
        public static IEnumerable<KeyValuePair<object, Func<object, object>>> Rocketize(params Expression<Func<object, object>>[] exprs)
        {
            return exprs.Select(expr =>
            {
                var key = expr.Parameters.FirstOrDefault() != null
                            ? expr.Parameters.FirstOrDefault().Name
                            : DateTime.Now.Ticks.ToString();
                return Rocketize(key, expr).First();
            });
        }
        public static IEnumerable<KeyValuePair<object, Func<object, object>>> Rocketize(object key, params Expression<Func<object, object>>[] exprs)
        {
            return exprs.Select(expr =>
            {
                var fn = expr.Compile();
                return new KeyValuePair<object, Func<object, object>>(key, fn);
            });
        }
    }
}

Now that we have this in place, we can run a few tests to show off the behavior. Note that I’ve opted for IEnumerables of KeyValuePair instead of a Dictionary or Hashtable. This just a personal preference, in that I wanted to support multiple objects with the same key within the data structure.

using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace HashRocket.Tests
{
    [TestClass]
    public class Tests
    {
        [TestMethod]
        public void Can_Convert_Lambda_Into_Kvp()
        {
            //arrange
            var testInput = "asdf";

            //act
            var rocket = __.Rocketize(input => input).First();

            //assert
            Assert.IsTrue(rocket.Key.Equals("input"));
            Assert.IsTrue(rocket.Value(testInput).Equals("asdf"));
        }

        [TestMethod]
        public void Can_Convert_Multiple_Lambdas_Into_Multiple_Kvps()
        {
            //arrange
            var testInputs = new object[] {"asdf","zxcv",2};

            //act
            var rockets = __.Rocketize(foo => foo + "qwer0",
                                       bar => bar + "qwer1",
                                       biz => biz + "qwer2").ToList();

            //assert
            for (var i = 0; i < rockets.Count; i++)
            {
                Assert.IsTrue(rockets[i].Value(testInputs[i]).Equals(testInputs[i] + "qwer" + i));
            }
        }
}

What’s surprisingly cool about this approach is that it becomes very easy create configuration objects with lambda syntax that can be passed directly into objects for initialization. If you’re familiar with this pattern in Ruby or JavaScript, you’ll appreciate the power and elegance it also affords to C#. To better understand the benefits and potential tradeoffs to using this trick, see Jeremy Skinner’s article on the topic.

Namaste..

Leave a comment