A DSL For Handling Zero, One, Many

All this talk about refactoring to clean up code and monads to create pipelines has put me on a code composition kick… I wrote this code today:

   1: private AddAssetResult HandleAssetFamily(SortContainer container, SystemAsset systemAsset)

   2: {

   3:     AddAssetResult result = null;

   4:     var assetFamilies = assetFamilyDao.GetByAssetType(systemAsset.AssetTypeId);

   5:  

   6:     if (assetFamilies == null || assetFamilies.Count == 0)

   7:     {

   8:         result = new AddAssetResult(SortContainerResources.NoAssetFamily);

   9:     }

  10:  

  11:     if (assetFamilies != null && assetFamilies.Count > 1)

  12:     {

  13:         result = new AddAssetResult(SortContainerResources.MultipleAssetFamilies);

  14:     }

  15:  

  16:     if (assetFamilies != null && assetFamilies.Count == 1)

  17:     {

  18:         var assetFamily = assetFamilies[0];

  19:         SetContainerFamily(container, assetFamily);

  20:  

  21:         result = VerifyFamilyIsCompatible(systemAsset, container);

  22:     }

  23:  

  24:     return result;

  25: }

Being the good little geeky me that I am, I wanted to use my shiny new monads hammer to pound this code into shape. As I was thinking about the monads that I have come across in my reading and my current understanding of them, though, I decided that I should probably see if this problem is actually a nail before whacking it with my hammer. So… rather than forcing myself to do something that might be dumb, I decided to approach this code from a composition standpoint – because after all, that’s one of the things a monad gives me.

 

Zero, One, Many

As I thought about the problem, I remembered an article that I read a long time about that talked about the three significant numbers: zero, one and many. The idea is that most of the time, you only need to pay attention to these three numbers. In looking at the above code, you can see that these are the exact numbers that I am paying attention to. This popped an idea for a DSL into my head and I pseudo-coded this into existence:

   1: var result = assetFamilies.CheckCount<AddAssetResult>()

   2:     .WhenZero(() => new AddAssetResult(SortContainerResources.NoAssetFamily))

   3:     .WhenOne(() => new AddAssetResult(SortContainerResources.MultipleAssetFamilies))

   4:     .WhenMany(AddAssetWithFamily);

The idea was to have a fluent interface / DSL that allows me to pay attention to the three significant numbers that I need in my above scenario and compose the behavior of the system by stating what would happen for each of those numbers. It seems like a pretty good idea and I like the syntax (all C# ceremony aside, of course).

 

Adjusting For Reality

I expected to have to adjust this code for reality, of course. I wrote that above example in notepad just to bang it out without worrying about proper syntax and the actual constraints of C#.

I found out that I needed to specify two generics parameters to the CheckCount extension method. I originally wanted to use IEnumerable as the “this” parameter of the extension method, but there is no Count on it and no way to get one without looping over the entire enumeration. So, I decided to accept that I needed to pass in an IEnumerable<T> as “this” to the extension method. That made the first line read like this:

   1: var result = assetFamilies.CheckCount<AssetFamily, AddAssetResult>()

Which should read like “check the count of asset families, and return an add asset result based on the count”.

The next thing I had to do was add a .Result property to the DSL so that I could get the result out of the method chains. I also had to adjust the WhenOne method I was calling and I realized that I had the WhenOne and WhenMany cases switched. Fixing those items makes the entire call chain look like this:

   1: var result = assetFamilies.CheckCount<AssetFamily, AddAssetResult>()

   2:     .WhenZero(() => new AddAssetResult(SortContainerResources.NoAssetFamily))

   3:     .WhenOne(() => AddAssetWithFamily(systemAsset, container, assetFamilies[0]))

   4:     .WhenMany(() => new AddAssetResult(SortContainerResources.MultipleAssetFamilies))

   5:     .Result;

Not too bad. It’s much better than the original code that I started with and I can live with this.

 

Implementing The DSL

I started with an extension method for the CheckCount call and then built out a small class called CountChecker. This class does the actual work of deciding which of the numeric methods should have it’s Func<T> called and stores the results of the one that is called. Since this is a method chaining situation, each of the When* methods that I chain will actually be called, but I only want to execute the Func<T> of the one that matches the actual count of the collection I’m examining. When that Fun<T> is called, I also want to store the result so that I can return it from the Result call at the end.

   1: public static class CountCheckerExtensions

   2: {

   3:     public static CountChecker<TResult> CheckCount<TCollectionType, TResult>(this IEnumerable<TCollectionType> enumerable)

   4:     {

   5:         int theCount = 0;

   6:         if (enumerable != null)

   7:             theCount = enumerable.Count();

   8:         return new CountChecker<TResult>(theCount);

   9:     }

  10: }

  11:  

  12: public class CountChecker<TResult>

  13: {

  14:     private readonly int theCount;

  15:  

  16:     public TResult Result { get; private set; }

  17:  

  18:     public CountChecker(int theCount)

  19:     {

  20:         this.theCount = theCount;

  21:     }

  22:  

  23:     public CountChecker<TResult> WhenZero(Func<TResult> zeroAction)

  24:     {

  25:         if (theCount == 0)

  26:             Result = zeroAction();

  27:  

  28:         return this;

  29:     }

  30:  

  31:     public CountChecker<TResult> WhenOne(Func<TResult> oneAction)

  32:     {

  33:         if (theCount == 1)

  34:             Result = oneAction();

  35:  

  36:         return this;

  37:     }

  38:     

  39:     public CountChecker<TResult> WhenMany(Func<TResult> manyAction)

  40:     {

  41:         if (theCount > 1)

  42:             Result = manyAction();

  43:  

  44:         return this;

  45:     }

  46: }

It’s a simple implementation. Nothing terribly fancy, other than getting the count out of the IEnumerable<T> and passing it into the CountChecker constructor.

 

Fluent Interface And DSL, But I Don’t Think This Is A Monad

I mentioned that I wanted to use my shiny new monad hammer on this problem, at the beginning of this post. In the end, I don’t think I did. I’m fairly certain that what I’ve implemented is just simple method chaining and/or a fluent interface to create a DSL. Does anyone have an opinion and expertise to say otherwise? I’d be interested in hearing why you think this is, or know it is not, a monad (I realize that I’m wearing monad colored glasses these days. I’m sure this obsession will pass soon enough).

 

Get The Source

If you’re interested in the source for this DSL and example, I put it up on Github’s gists. You can grab it all, here: https://gist.github.com/5cb0241ea0123b76fbb1


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#, DSL. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://fernandozamorajimenez.blogspot.com Fernando Zamora

    Cool implementation. It looks like a chain of responsibility pattern. Can it be considered a chain of responsibility pattern. I know that the chain of responsibility pattern can be extended so maybe it is not one. Would this be considered a chain of responsiblilty implementation?

  • http://fernandozamorajimenez.blogspot.com Fernando Zamora

    BTW if it is a chaing of responsiblity implemenation. It is much cleaner than the one given by wikipedia.

  • Omer Mor

    1. Your counting method is not efficient at best and incorrect at worst: it’ll iterate the entire collection, which is incompatible with infinite collections (think iterator block that yields all even numbers). Instead you should use the .Any() extension to differentiate between 0 and >0, and then .Skip(1).Any() to differentiate between 1 and >1.

    2. I don’t think it is a monad, because it does not allow you to chain computations on it that “stays in the monad” – the methods that returns the monad in your example does not add computations and does not mutate the monad. This leaves the monad immutable which I believe disqualifies it as a monad. But I might be wrong – I have no FP background.

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

    Omer –

    thanks for the advice on .Any()… i had not thought about an infinite collection. though i wonder – shouldn’t the .Count extension of IEnumerable account for this? i’ll try out your suggestion, though. i think it will have a nice effect on the overall modeling in this code.

    and i agree with your reasons that this isn’t a monad. i was looking at it again, last night, and was thinking similar things – though i couldn’t quite express it clearly in my head.

  • Omer Mor

    The .Count extension needs to return the numbers of items, which is more information than you need (which is 0/1/*). To obtain that number it has to iterate the entire enumerable (unless the underlying type is an array or collection, in which case it takes a shurtcut).

  • http://www.tavaresstudios.com Chris Tavares

    Silly philosophical question time: does it matter if it’s actually a monad or not? It’s a useful snippet that helps code clarity. Is there some ‘monadic theory’ that we would gain additional advantages from if it were, indeed, a monad? Assuming it isn’t?

    I still don’t quite get the monad concept, and Haskell gives me a headache, so I’m still struggling myself.

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

    Chris – it honestly makes no difference if it’s a monad or not. it wouldn’t add anything if it was. it was only a curiosity in my head since i had recently been learning about monads.

    the code stands on it’s own, for what it is, whether or not it is or is not anything nameable. :)