Scott Hanselman

The Weekly Source Code 14 - Fluent Interface Edition

January 31, 2008 Comment on this post [20] Posted in ASP.NET | Learning .NET | Programming | Ruby | Source Code
Sponsored By

iStock_000000237891XSmall5 If you're new to this, each week I post some snippets of particularly interesting (read: beautiful, ugly, clever, obscene) source and the project it came from. This started from a belief that reading source is as important (or more so) as writing it. We read computer books to become better programmers, but unless you're reading books like Programming Pearls, you ought to peruse some Open Source projects for inspiration.

And so, Dear Reader, I present to you fourteenth in a infinite number of posts of "The Weekly Source Code." Here's some source I was reading this week.

Over the last year or so I've seen an increase in discussion around so-called "fluent interfaces" in many languages. The addition of extension methods (mixins) to C# 3.0 has caused a flood of interesting (weird?) interface as we as a collective to attempt to make programming as easy as writing prose.

Martin Fowler talked about this in 2005 after a workshop with Eric Evans when they first named them "fluent interfaces." He gives this example:

The simplest example is probably from Eric's timeAndMoney library. To make a time interval in the usual way we might see something like this:

TimePoint fiveOClock, sixOClock;
...
TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);

The timeAndMoney library user would do it this way:

   TimeInterval meetingTime = fiveOClock.until(sixOClock);

Of course, the ubiquitous Ruby example is

20.minutes.ago

Martin makes a great point when trying to put "fluent" APIs in a common OOP context, like that of an object browser with emphasis mine:

One of the problems of methods in a fluent interface is that they don't make much sense on their own. Looking at a method browser of method by method documentation doesn't show much sense to with. Indeed sitting there on its own I'd argue that it's a badly named method that doesn't communicate its intent at all well. It's only in the context of the fluent action that it shows its strengths. One way around this may be to use builder objects that are only used in this context. - Martin Fowler

Piers Cawley follows up and offers a number of guidelines for use when one is designing these things. See his post for the complete annotated list.

  1. Hide your working.
  2. Keep your state to yourself.
  3. Think really hard about names.
  4. Take advantage of your implementation language.
  5. If you have them, blocks are you friends.
  6. Test first design can be a useful way of exploring what your interface should be.
  7. Reasonable defaults.

In the .NET space, Ayende's Rhino Mocks are, I think, the first and best example before LINQ that really got it right with syntax like.

  Expect
.Call(mock.GetID(1))
.IgnoreArguments()
.Repeat
.Once()
.Return(something);

Similar things are done in Java with their support for mixins, called Static Imports in Java 5.

When fluent interfaces get larger and more complex, they suddenly get called Domain Specific Languages as Peirs points out. But, a true DSL is even easier and might not be fluent at all, but rather customized to the domain:

"It seems that every time someone writes a Ruby library that uses class methods, symbols and hashes reasonably sensibly they get delusions of grandeur and call the result a Domain Specific Language (or maybe an ‘embedded’ DSL)."

Two good examples of libraries as DSLs with some fluent aspects are Why's Hpricot, an HTML Parser for Ruby that looks like this:

 #!ruby
 require 'hpricot'
 require 'open-uri'
 # load the RedHanded home page
 doc = Hpricot(open("http://redhanded.hobix.com/index.html"))
 # change the CSS class on links
 (doc/"span.entryPermalink").set("class", "newLinks")
 # remove the sidebar
 (doc/"#sidebar").remove
 # print the altered HTML
 puts doc

And Python's Beautiful Soup, also an HTML Parser.

 from BeautifulSoup import BeautifulSoup, Tag
 soup = BeautifulSoup("Argh!FooBlah!")
 tag = Tag(soup, "newTag", [("id", 1)])
 tag.insert(0, "Hooray!")
 soup.a.replaceWith(tag)
 print soup
 # Argh!Hooray!Blah!

Back on the C# side, Garry Shutler is creating more fluent assertions using extension methods and lambdas for MBUnit like:

testObject.ShouldBeTheSameObjectAs(targetObject).And.ShouldBeEqualTo(testObject).And.ShouldSatisfy(x => x is Object);

But what's a DSL and what's a Fluent Interface and what's just an API? Martin adds in 2006 (as he continues to write his DSL Book):

For me, a key element is that DSLs are limited both in scope (they refer to a particular domain) and capability (they lack features that are basic for general purpose languages). As a result good DSLs are usually small and simple: hence terms like 'little languages' and 'mini-languages'.

For internal DSLs, the fuzzy boundary is what is an API and what is a DSL. Fundamentally there is no difference, an internal DSL is just an API with a fancy name (as the old Bell labs saying goes: "library design is language design"). Despite this, however, I think there is a different feel when you are working with an API that's written with a DSL feel. Things like a FluentInterface can make working with an API a qualitatively different experience. Thinking in DSL terms makes you think about readability in a different way, exploiting the syntax of the host language to create something that seems to stand on its own - rake is a great example of this. - Martin Fowler

Even scripting languages like PHP are getting on board with fluent interfaces, assuming that "with" in this context makes sense to you.

<?php
private function makeFluent(Customer $customer) {
    $customer->  newOrder()
         ->with(6, 'TAL')
         ->with(5, 'HPK')->skippable()
         ->with(3, 'LGV')
         ->priorityRush();           

Ultimately I think Paul Jones nails it when he says "Fluent Interfaces Require Fluent Situations":

"I think, for a fluent interface to be effective, you need situations where you actually have all that information at one time so that you can chain the methods in a fluid way" - Paul M. Jones

Scott Bellware said matter of factly:

"Whether fluent interface is a form of DSL or not, it's obviously a form of fluent interface." - Scott Bellware

Are you exploring Fluent Interfaces?


Shameless Plug: As an aside, one addition thing I'd like to mention is our little Forums over at http://hanselman.com/forum. They are run on excellent JitBit's AspNetForum application, the "little forum that could" in my opinion. There's lots of interesting discussions on many diverse topics, and you can look at just the most recent posts, and every page has an RSS Feed.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
January 31, 2008 12:42
static class FluentUtil
{
static public TimeSpan Minutes(this int minutes) { return TimeSpan.FromMinutes(minutes); }
static public DateTime Ago(this TimeSpan ago) { return DateTime.Now.Subtract(ago); }
}

<System.Runtime.CompilerServices.Extension()> _
Function Minutes(ByVal value As Integer) As TimeSpan
Return TimeSpan.FromMinutes(value)
End Function

<System.Runtime.CompilerServices.Extension()> _
Function Ago(ByVal value As TimeSpan) As DateTime
Return DateTime.Now.Subtract(value)
End Function
January 31, 2008 13:16
I wonder what non-native English speakers experience of using and writing fluent interfaces are...

[)amien
January 31, 2008 14:57
That's where it would be nice if they added MethodMissing functionality to C# and VB.NET. Then you could add translations. Overcoming the left-to-right bias in programming languages might be trickier.
January 31, 2008 14:57
very Interesting!
January 31, 2008 17:10
I guess it depends on what you are used to. For me the first line:

TimeInterval meetingTime = new TimeInterval(fiveOClock, sixOClock);


was easier to comprehend than the second line:

TimeInterval meetingTime = fiveOClock.until(sixOClock);


because I knew I am looking at source code. Hmm...

I guess it is the same thing that I feel it is easier to search in Google with disjointed keywords than using natural language search.
January 31, 2008 17:49
What does programming or computing have to do with Zen?

David
January 31, 2008 19:19
Joe Chung is making a good point in his example. Does anyone seriously think the DSL version is easier to understand that the traditional OO version? Or are you just complicating things by creating a larger framework to learn and maintain?

DateTime startTime = ...;
DateTime endTime = ...;
TimeSpan meetingTime = endTime.Subtract(startTime); //<- current supported method in .net
TimeSpan meetingTime2 = startTime.Until(endTime); //<- pointless duplicate with same result

All you've done in this case is rename the method and reverse the direction of the method operation. I find DSL to be academically "interesting" as a theory, but I have serious doubts that anyone is going to make a thourough enough DSL implementation that the sales guy (or any non-engineer minded person) can pick it up and just start writing english (or preferred language) to create an application.

Why not just learn to write in yoda speak instead of natural language. Doesn't everyone think yoda is cool?

20.minutes.ago
in "yoda speak":
Minutes.Ago(20) //<- Minutes class, with method Ago

Talking about all these simple scenarios makes me wonder if George Lucas ever did any OO programming?
January 31, 2008 19:58
I just added a SQL() class to my C# codebase at work, allowing you to do:

foreach (IDataReader rec in new SQL(database_link).query(
"SELECT blah... WHERE x=?, y=?, z=?", p1, p2, p3))
Console.out.writeline(rec.GetInt64(0).ToString());


Which hides all kinds of cruft under the covers:

using(dbconnection db = new dbconnection(database_link))
{
using (IDataReader rec = db.query("sql", new ArrayList(new object[] {p1, p2, p3})))
{
while (rec.Read())
Console.out.writeline(rec.GetInt64(0));

I posted about it on my blog.
January 31, 2008 20:23
When you are talking about fluent interfaces, the thing that leaps to mind is a javascript library called JQuery. You can do stuff like $("div").find("p").hide("slow") (finds all <p> tags inside divs and hides them with an animation)
January 31, 2008 21:35
I like the idea of fluent interfaces. I wrote some extension methods to clean up code that deals with tables.



public static class TableExtensions
{
. . public static TableRow AddRow(this Table table)
. . {
. . . . if (table == null)
. . . . {
. . . . . . throw new ArgumentNullException("table");
. . . . }
. . . . TableRow row = new TableRow();
. . . . table.Rows.Add(row);
. . . . return row;
. . }

. . public static TableRow AddRow(this Table table, params Control[] children)
. . {
. . . . if (table == null)
. . . . {
. . . . . . throw new ArgumentNullException("table");
. . . . }
. . . . TableRow row = table.AddRow();
. . . . Array.ForEach<Control>(children, c => row.AddCell(c));
. . . . return row;
. . }

. . public static TableCell AddCell(this TableRow row)
. . {
. . . . if (row == null)
. . . . {
. . . . . . throw new ArgumentNullException("row");
. . . . }
. . . . TableCell cell = new TableCell();
. . . . row.Cells.Add(cell);
. . . . return cell;
. . }

. . public static TableCell AddCell(this TableRow row, Control child)
. . {
. . . . if (row == null)
. . . . {
. . . . . . throw new ArgumentNullException("row");
. . . . }
. . . . return row.AddCell().AddChildControl(child);
. . }

. . public static TableCell AddChildControl(this TableCell cell, Control child)
. . {
. . . . if (cell == null)
. . . . {
. . . . . . throw new ArgumentNullException("cell");
. . . . }
. . . . cell.Controls.Add(child);
. . . . return cell;
. . }
}

public static class GenericWebControlExtensions
{
. . public static T SetFontSize<T>(this T control, FontUnit fontSize) where T : WebControl
. . {
. . . . control.Font.Size = fontSize;
. . . . return control;
. . }

. . public static T SetFontBold<T>(this T control, bool isbold) where T : WebControl
. . {
. . . . control.Font.Bold = isbold;
. . . . return control;
. . }
}


This allows me to write some pretty clean code like so


Table table = new Table();

table.AddRow(new LiteralControl("Select a User"))
.SetFontSize(FontUnit.Parse("10px"))
.SetFontBold(true);

table.AddRow(userList);

this.userList = new DropDownList();
userList.Items.Add("steve");
userList.Items.Add("aaron");


I like it... and I think it might qualify as a fluent interface.
January 31, 2008 21:39
opps copy and paste error on that last code snippit

Table table = new Table();

table.AddRow(new LiteralControl("Select a User"))
.SetFontSize(FontUnit.Parse("10px"))
.SetFontBold(true);

this.userList = new DropDownList();
userList.Items.Add("steve");
userList.Items.Add("aaron");

table.AddRow(userList);

January 31, 2008 22:55
David - What does programming or computing have to do with Zen? you ask?

Well, a koan is a kind of story, just as code is a story. Blogs are stories that never end, and an ongoing dialog. Note however, when I say 善/Zen" I mean Nice, Good, Postive. Arguably I could have used 禪/Zen to mean Buddist-like Enlightenment, but I like that 善 is pronounced (more or less) like "Zen." So, the intent is "computer goodness" or "computer positivity".

Wikipedia has a nice sentence when they say koans "contain...aspects that are inaccessible to rational understanding, yet may be accessible to intuition." again, much like code.
February 01, 2008 1:14
It's definitely something worth exploring. Chaining calls is certainly nice, and makes you forget that you wished C# had a "with" statement when moving from VB such as myself:
// IIRC
With myvar
.Value = 25
.CouldYouInvokeMethodsIDontRemember()
End With

If you start going the path of immutable objects (which is good IMO), then you naturally start getting into chaining actions, as each step returns a new instance you can further act on, without losing the "flow". It then makes sense to start making the syntax look like you're just expressing in english the actions on the object being manipulated. For example:
string value = ....

string linkName = value.Normalize().Capitalize().Split(' ').Join('_');

(no need to explain what this does ;), and I didn't break the lines before each subsequent dot as the code rendering got too big).

The combination with extension methods is specially powerful since it allows you to turn existing (and maybe harder to use) APIs (i.e. CodeDom) more amenable. And this might spread an entire ecosystem of extensions to built-in .NET types for alternative coding styles.

It can also get quite messy if you're not consistent or various people in the team start using different extension methods over the same underlying APIs... so you might have to watch out for that and have some standards.

There's also another side-effect which is that people using the fluent interface don't really need to understand what the intermediate objects are for, neither they need to be exposed to anything but their interfaces (that make subsequent "fluent" invocations possible). Think of Moq API:

var mock = new Mock<IFoo>();

mock.Expect(foo => foo.Add(1, 5)).Returns(6);
// will only return 6 if the Add method is called with a 1 and a 5.
// all other cases will throw


Here, the user doesn't ever interact directly with the ICallReturn interface (ICall for void methods expectations). Intellisense and type inferencing work magic in this case where setting an expectation for a void-returning method automatically makes the Returns "fluent action" seem to "dissapear" as it no longer makes sense given the previous context of the "fluent sentence".

It might even be a good idea to hide all those "fluent intermediaries" as available types to declare, so that the user doesn't even see them when declaring variables and such.

// This effectively hides the interface from VS intellisense when declaring types
// Works only if you're linking to the binaries, not the project
[EditorBrowsable(EditorBrowsableState.Never)]
public interface ICall
{
}


Users still get to browse the type in Reflector (not in VS object browser though :( ), get signature and documentation tooltips on it, etc. And they could definitely declare a variable of this type, but they'd have to know the type name and type it manually.

Kind of reduces the API surface without actually reducing it ;). I think I'm gonna use that attribute on Moq...


Anyway, C# 3.0 is so much fun than 2.0 ever was...
February 01, 2008 1:39
BTW, the link to Fowler's FluentInterface page is broken.
It should be http://martinfowler.com/bliki/FluentInterface.html
February 01, 2008 2:03
I've been trying to implement these recently in my own apps. I find them highly useful for things like dependency resolution when I may want the defaults sometimes but other times I may wish to inject specific factories or other classes (makes testing really easy too). I find that when I'm writing one, it's usually easiest for me to write the test first so I can decide exactly how I want to talk to my object that will have a fluent interface. Then, I can just write the code that I want to call and then figure out how to make the interface work for that code. It takes some getting used to but once you start just writing the method calls themselves first, you'll find it's much easier to implement.
February 01, 2008 5:05
Yea for jQuery! For example: $("p.surprise").addClass("ohmy").show("slow"); means get all the p elements of class surprise, add to them the ohmy class, then change their display from hidden to block with a slow animation. It's the most natural javascript library out there: http://jquery.com/
February 01, 2008 5:49

Why not just learn to write in yoda speak instead of natural language. Doesn't everyone think yoda is cool?

20.minutes.ago
in "yoda speak":
Minutes.Ago(20) //<- Minutes class, with method Ago
</block>

When You're discussing implementation with Domain experts and not other programmers Fluent interfaces can become quite useful, not to mention if you want to implement any kind of scripting capabilities so you don't have to always re-compile.

The example given:

DateTime startTime = ...;
DateTime endTime = ...;
TimeSpan meetingTime = endTime.Subtract(startTime); //<- current supported method in .net
TimeSpan meetingTime2 = startTime.Until(endTime); //<- pointless duplicate with same result

I would do the second one as something like:

TimeSpan meetingLength = startTime.To(endTime)

That makes it much more declartive, we don't care what it does (in this case a subtraction of startTime from endTime), we're telling it what we /want/.


"Whether fluent interface is a form of DSL or not, it's obviously a form of fluent interface." - Scott Bellware


The link to Bellware's blog is busted since he left CodeBetter. All his content has been deleted.
February 01, 2008 11:02
With Extension methods Microsoft has created the monster!
We will need lot of time to recover and start to use them in the right way.
February 01, 2008 13:53
Thanks for the reply Scott I had assumed you meant the latter meaning of the word.

David.
February 12, 2008 8:52
I'm surprised you didn't drop SubSonic's name as another .NET library that uses a fluent interface, namely the Query/Query2 objects so you do things like:

ProductCollection p = Northwind.DB.Select()
.From<Northwind.Product>()
.InnerJoin<Northwind.Category>()
.Where(“CategoryName”).Like(“c%”)
.ExecuteAsCollection<Northwind.ProductCollection>();

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.