Internal DSLs, method chaining and discoverability

Paul says “The whole .NET space has gone fluent interface crazy”, and he is quite right. Everybody has their own fluent interfaces, and unless I’m missing the bigger picture, most of them seem to be about productivity over discoverability. The intent is clear, write more intent-revealing code in less time.

More often than not however, you multiply the entry points needed to be known by developers. Let’s take an example with the criteria API in nHibernate.

return Session.CreateCriteria(typeof(FundingCategory), "fc")
   
.CreateCriteria("FundingPrograms", "fp")
   
.CreateCriteria("Projects", "p", JoinType.LeftOuterJoin)
   
.Add(Restrictions.Disjunction()
       
.Add(Restrictions.Eq("fp.Recipient.Id", recipientId))
       
.Add(Restrictions.Eq("p.Recipient.Id", recipientId))
   
)
   
.SetProjection(Projections.ProjectionList()
       
.Add(Projections.GroupProperty("fc.Name"), "fcn")
       
.Add(Projections.Sum("fp.ObligatedAmount"), "fpo")
       
.Add(Projections.Sum("p.ObligatedAmount"), "po")
   
)
   
.AddOrder(Order.Desc("fpo"))
   
.AddOrder(Order.Desc("po"))
   
.AddOrder(Order.Asc("fcn"))
   
.List<object[]>();
[nitpicker corner: no one ever said the criteria api was a fluent api, it’s at most method chaining.]

I’ve highlighted in red those entry points. Each of those method usually takes an instance of an interface. A static method is then used to create those objects. What’s the issue with that?

It all comes down to discoverability. One of the biggest issue I’ve always had with the criteria API is the low discoverability of those types. Take the ICriterion interface, for which the Expression class provides the simple criterions. The only way, once in a method, for me to know that where it expects ICriterion I could use Expression is by going to the documentation. This is for me a massive discoverability failure.

The second issue comes from the latest lambda-based APIs. Let’s have a bit of code from the original blog post from Dru that Paul was referring to.

  1.  static TestDeployment()  
  2.     {  
  3.         Define(() =>  
  4.             {  
  5.                 During(Web, (p) =>  
  6.                     {  
  7.                         p.OnServer("WebServer")  
  8.                             .IisSite("Apps")  
  9.                             .VirtualDirectory("dashboard")  
  10.                             .Verify()  
  11.                             .CreateIfItDoesntExist();  

I’ve not downloaded the code to have a play, but I assume that Define and During are actually properties on the base type. On a positive note, this does help with discoverability, at the cost of enforcing an inheritance hierarchy.

So why do I feel awkward with this syntax? The nesting of lambdas. The multiplication of p, x and other one-letter prefix make the code much less readable. The pen*s operator is not exactly my favourite addition to the C# compiler.

Here’s my checklist for what constitutes a nice usable fluent API. [nitpicker corner: OpenRasta doesn’t do it that way everywhere. You didn’t say the same thing six months ago].

  • No lambda-based chaining
  • No multi-line lambda expressions
  • At most one or two static class entry-points to discover
  • Method parameters should only be primitive types

There is a way to make a fluent API while respecting those points, but I believe the result would be better. So here’s Dru’s example rewritten using those guidelines.

using(Definition)
{
    During.Web
        .Server("WebServer")
            .Has.IisSite("Apps")
                .VirtualDirectory("dashboard")
                .Verify()
                .CreateIfItDoesntExist()
        .And
        .Server("SvrTopeka19")
            .Has.Msmsq()
                .PrivateQueueNamed("mt_subscriptions")
                .CreateIfItDoesntExist()
        .And
        .CopyFrom(@".\code_drop\dashboard\**\*.*").To(@"\\webserver\apps\dashboard\");
}

The main difference here is the introduction of scoping operators. The Server method returns two properties, And which closes the current Server scope, and Had Has which open a new service-specific scope.

I find this kind of API much more readable, and they introduce less syntax noise (aka no lambda operators or blocks). There is also no need to introduce external points of references, be them be properties or static classes, except for the original entry point. Arguably, they’re harder to write, but I think the effort is worth the benefits.

Ads

Summer speaking schedule, AltNetBeers, the alt.net conference, oh my!

Wow, who would’ve thought the first conference season would’ve been so intense! I had an absolute blast meeting people, presenting, chatting away and drinking nice beers with so many interesting people! WebDD, DDD Scotland and DDD Belfast have been a blast!

I also realize that I’ve delivered all the talks and workshops I announced in my last schedule, so time for an update, both on my talks and on the interesting conferences and events that are being organized.

  • Thursday 28th May – London (DNUG)
    Presenting WebForms vs. MVC, with my dear friend Phil Winstanley
  • Wednesday 17th June
    Organizing AltNetBeers #8 (Glass House in Soho)
  • Tuesday 23nd June – Bracknell (VBUG)  
    Wednesday 24th June – Cork (IT@cork)  
    Thursday 25th June – Dublin (Agile Ireland)

    Presenting When agile goes bad: how to stay calm and move forward
  • Tuesday 14th July – Nuneaton (VBUG)
    Presenting When agile goes bad: how to stay calm and move forward
  • Thursday 15th July – London (Skills Matter – OpenSource.net exchange)
    Presenting What OpenRasta does other frameworks can’t
  • Friday 31st July – Sunday 2nd August
    The alt.net conference weekend with the AltNetBeers #10 – Conf edition I’m organizing
  • Monday 17th August – Coventry (NxgGenUG)
    @serialseb’s MVC Best Practices
  • Wednesday 19th August – London (The Edge User Group – formerly known as VistaSquad)
    When agile goes bad: how to stay calm and move forward

There you go. May have more to announce if things happen, or may not, but it’s going to be a busy summer!

Ads

How some agencies make it hard to trust them

I just received an email from Computer People, telling me “we agreed that I would keep in touch and update you with any further opportunities and developments around SharePoint”. I’m pretty sure that’s factually inaccurate, as I have very little interest in SharePoint development and certainly wouldn’t agree on the phone to be contacted for marketing reasons. But fair enough, if it was to help me find new clients, I wouldn’t be that bothered. But no! It’s to try and sell me sharepoint seminars! For Christ’s bullocks’ sake!

So off to my sent items, in which I keep years and years of emails (roughly since 2001), and of course I find absolutely no mention of that Dan Freshwater that just sent me an email, so emails are out.

Leaves only their website, maybe I subscribed to that and forgot to check the “do not contact me for marketing purposes” option. Although I’m very anal about always making sure I check that box, on paper forms or online, mistake is human so I may have not seen it. Or maybe it wasn’t there. Who knows.

The email goes on to try and sell me a seminar for SharePoint. So off to the website to check Computer People’s privacy policy, and here’s what I found.

You can exercise the right at any time by contacting us at Legal Services, 71 Elstree Way, Borehamwood, Hertfordshire, WD6 1WD.

What, legal services doesn’t have computers? Designing an option in your profile was too hard? Or are you making this difficult on purpose? Seeing as you lost my trust at the very beginning by saying I asked to receive marketing information about SharePoint, I’ll go for the last option.

Email was sent asking to not ever receive marketing emails again. We shall see if it has an effect, and I’ll be sure to report.

Ads