mscommunity.net

Interactive mscommunity.net online activities
Signed in as anonymous | Edit Profile | Sign out | Help
in Search

Weblog :: Boris Ševo

Sporadic posts about my interests, e.g. software development (mostly .NET), technology in general and some occasional rant.

prosinac 2007 - Posts

  • Internal DSLs, fluent interfaces and extension methods in C#

    This is one of my longest blog posts and because of that I will start with a short list which describes what I will cover in this post:

    • internal DSLs
    • fluent interfaces
    • method chaining
    • extension methods

    Few weeks ago I found Neal Ford's session material about Building DSLs in Static and Dynamic Languages. This is a material from one No Fluff Just Stuff symposium - Great Lakes Software Symposium which is as far as I can see a really great conference with a tones of interesting sessions. (You can also found a lot of interesting materials at Neal's Conference Slides & Samples section.)

    Internal DSLs and Fluent Interfaces

    If you take a look at a NFJS web page you can see that they have a book called a NFJS anthology. Old copy (2006) of this book has a topic DSLs and Language-Oriented Programming by Neal Ford. In this topic Neal wrote:

    A domain-specific language is a limited form of computer language designed for a specific class of problems. ... Two types of DSLs exist: internal and external. An internal DSL is a internal domain-specific language written "on top" of the underlying syntax of your base language. If Java is your base language, then an internal DSL written in Java uses Java syntax to define the language.

    (more info about DSLs: Martin Fowler's DSLReadings)

    External DSLs were clear to me before, because sometimes when you are faced with a problem from a specific domain, building a whole new language can really help you to express this problem. If you are building an external DSL you are building a language with a syntax which is diffrent then your base language syntax. Hence, you need to build a lexer and parser for your external DSL.
    But, the purpose of internal DSLs wasn't so clear to me. I was asking myself why on hell would I like to build another language "on top" of my base language? Well, the answer lies in something which is called fluent interface (Martin Fowler's original bliki entry coining the term). The term fluent interface and internal DSL are in many ways synonyms. The whole concept can be reduced to a single sentence from a Martin Fowler's text:

    The API is primarily designed to be readable and to flow.

    So when you are designing an API to be fluent you are building a code which intent is to look like a well-formed sentences. The best way to describe this is probably by example. Imagaine that you have a following two classes:

        public class Appointment
    {
    private string _name;
    private DateTime _startTime;
    private DateTime _endTime;

    public string Name
    {
    get { return this._name; }
    set { this._name = value; }
    }

    public DateTime StartTime
    {
    get { return this._startTime; }
    set { this._startTime = value; }
    }

    public DateTime EndTime
    {
    get { return this._endTime; }
    set { this._endTime = value; }
    }
    }

    public class AppointmentCalender
    {
    private List<Appointment> _apponitments;

    public AppointmentCalender()
    { _apponitments = new List<Appointment>(); }

    public void Add(Appointment app)
    { _apponitments.Add(app); }

    public string Print()
    { ... }
    }

    If we want to remember that we have a dentist in 5 days from 4PM to 6PM in non-fluent way we will probably do something like this:

    	//"dentist" in 5 days from 4PM to 6PM
    AppointmentCalender appCalender =
    new AppointmentCalender();

    Appointment app1 = new Appointment();
    DateTime dt1 = new DateTime(DateTime.Now.Year,
    DateTime.Now.Month,
    DateTime.Now.Day + 5,
    16, 0, 0);
    app1.Name = "dentist";
    app1.StartTime = dt1;
    app1.EndTime = dt1.AddHours(2);
    appCalender.Add(app1);

    Or if you think that this setters are requiring to much work you can write appropriate constructor and then you have something like this:

    	DateTime dt1 = new DateTime(DateTime.Now.Year,
    DateTime.Now.Month,
    DateTime.Now.Day + 5,
    16, 0, 0);
    appCalender.Add(new Appointment("dentist", dt1, dt1.AddHours(2)));

    Although the second example is conciser then the first one, initialization of dentist's start termine is still extremely ugly. The constructor saved us from some work but without a deeper look at constructor's implementation it's hard to say what is the meaning and purpose of all its parameters. In first example we didn't have this problem because from setter's names (Name, StartTime and EndTime) it was clear what exactly the code is doing. From above two examples we can see that we have two candidates which need to be more fluent. First we will make initialization of the DataTime object more fluent and then we will do the same with the initialization of the Appointment object.

    How to make your API more fluent?

    Before C# 3.0 was introduced if you want to make your API more fluent you were limited to the method chaining. In method chaining you are making the modifier methods return the host object so that multiple modifiers can be invoked in a single expression. Although, this is something which can help you a lot while building the fluent interface, it is also bringing one bad habit in your API about which you can read more in the next section. In C# 3.0 there is a new concept called extension methods. There is many info about extension methods on a web and if you don't know what they are you can find more info about them on Scott Guthrie's blog post New "Orcas" Language Feature: Extension Methods. Here I will just give a short definition from this Scott Guthrie's post:

    Extension methods allow developers to add new methods to the public contract of an existing CLR type, without having to sub-class it or recompile the original type. Extension Methods help blend the flexibility of "duck typing" support popular within dynamic languages today with the performance and compile-time validation of strongly-typed languages.

    For us, extension methods are specially usefull because they can help us make DataTime more fluent. We can accomplish this by extending DateTime CLR type with few methods. Here is the code which is doing exactly that:

        public static class DataTimeExtMethods
    {
    public static TimeSpan days(this int number)
    {
    return new TimeSpan(number, 0, 0, 0);
    }

    public static DateTime fromToday(this TimeSpan ts)
    {
    DateTime dt = new DateTime(DateTime.Now.Year,
    DateTime.Now.Month,
    DateTime.Now.Day);
    return dt.Add(ts);
    }

    public static DateTime at(this DateTime dt, int hour)
    {
    return new DateTime(dt.Year, dt.Month, dt.Day, hour, 0, 0);
    }

    public static int pm(this int hour)
    {
    if (hour > 0 && hour < 13)
    return hour + 12;
    else
    throw new ArgumentException("Wrong hour value");
    }
    }

    Now we can write something like this:

    	DateTime dt = 5.days().fromToday().at(4.pm());

    Writing 5.days().fromToday() before extension methods were introduced, were impossible because there was no way to extend a value type like integer. This kind of syntax is something natural to dynamic-typed languages so in Groovy it is possible to write 5.days.fromoday.at(4.pm). Because the use of extension methods isn't limited to value types only, we can also extend Appointment class with them. Nevertheless let's see how we can make Appointement class more fluent with a help of method chaining.

        public class Appointment
    {
    ...

    public Appointment Having(string name)
    {
    this.Name = name;
    return this;
    }

    public Appointment From(DateTime date)
    {
    this.StartTime = date;
    return this;
    }

    public Appointment To(DateTime date)
    {
    this.EndTime = date;
    return this;
    }

    public Appointment At(DateTime date)
    {
    this.StartTime = this.EndTime = date;
    return this;
    }
    }

    Now if we have a dentist in 5 days from 4PM to 6PM and birthday party in 17 days we can write:

    	AppointmentCalender appCalender = new AppointmentCalender();

    //fluent way
    DateTime dt = 5.days().fromToday().at(4.pm());
    appCalender.Add(new Appointment().Having("dentist").
    From(dt).
    To(dt.AddHours(2)));

    appCalender.Add(new Appointment().Having("birthday party").
    At(17.days().fromToday()));

    Pros and cons

    As we can see from above examples fluent interfaces can make your code more human readable. The price of this readability is more effort in API construction. The simple ("primitive") API of constructors, setters and additional methods is easier to write but harder to read. Better readability isn't worth the effort in all cases. In my opinion there are two places were better readability is worth the effort in time and thinking and that are library and framework construction.

    Effort in time and thinking isn't the only negative side in building fluent APIs. In method chaining we are using modifier methods which are returning the object which they modify although the common convention is that modifier methods are void (Command query separation). The use of extension methods is also bringing some potential problems. To ensure that you can't hijack or subvert the intended behavior of existing methods CLR will always prefer an instance method over an extension method. Although this is a good thing it also causes one problem known as versioning problem.

Powered by Community Server (Commercial Edition), by Telligent Systems