I guess I’ve been living under a rock for the last year, but I’ve only recently started using a couple of fluent APIs. A quick search of the internet and fluent APIs are popping up for everything from twitter to orm mapping to dependency injection.
So far I buy into the advantages of fluent interfaces over traditional property setting and long lists of function parameters. I find the consuming code easier to read, more intentful, easier to mix and match behavior, and yes, more fluid.
So let’s throw together a trivial fluent interface and examine how we consume it from a couple of different .Net languages.
public interface IRectangleFluent { IRectangleFluent SetHeight(int height); IRectangleFluent SetLength(int length); IRectangleFluent SetColor(Color color); } public class RectangleFluent : IRectangleFluent { Color color; int height; int length; public RectangleFluent() { } public IRectangleFluent SetColor(Color color) { this.color = color; return this; } public IRectangleFluent SetHeight(int height) { this.height = height; return this; } public IRectangleFluent SetLength(int length) { this.length = length; return this; } }
Basically, instead of properties, we’re using methods to return the current context for the next call in the method chain. To consume our fluent interface from c# we simply write
var rect = new RectangleFluent() .SetColor(Color.AliceBlue) .SetHeight(16) .SetLength(16);
From VB we write
Dim rect = (New RectangleFluent()) _ .SetColor(Color.AliceBlue) _ .SetHeight(16) _ .SetLength(16)
Not quite as pretty, as we have to wrap the construction of our instance in an additional set of parenthesis, and of course we have the infamous line continuations. I could have sworn i read something about line continuations in VB being smarter, i guess not smart enough to realize we want to call our method on the next line.
The additional set of parenthesis are needed in f# as well
let rect = (new RectangleFluent()).SetColor(Color.AliceBlue).SetHeight(16).SetLength(16)
although we can write
let rect = RectangleFluent().SetColor(Color.AliceBlue).SetHeight(16).SetLength(16)
which I really don’t like as it’s very difficult to tell whether an object is being created or a static method is being called.
Luckily, a common practice in fluent libraries is to use a static factory method for creation of object instances, which standardizes the construction of the instance across all three languages. Adding a factory method to our definition and changing the constructor to private
private RectangleFluent() { } public static IRectangleFluent Create() { return new RectangleFluent(); }
allows us to write
var rect = RectangleFluent.Create() .SetColor(Color.AliceBlue) .SetHeight(16) .SetLength(16); Dim rect = RectangleFluent.Create() _ .SetColor(Color.AliceBlue) _ .SetHeight(16) _ .SetLength(16) let rect = RectangleFluent.Create().SetColor(Color.AliceBlue).SetHeight(16).SetLength(16)
The c# and vb allow for very readable consuming code, but I’m embarrassed to admit I can’t figure out how to format the f# code for readability. I’ve looked everywhere and I can’t find an example of calling a method in a method chain on the next line, at least VB’s unsmart line continuation characters let us do it. With the abundance of fluent APIs on the horizon, and f# being pitched as the hot new language for VS 2010, I’m sure I’m overlooking something obvious.

Hi Matthew,
I think the reason that fluent APIs look ugly in F# is that they do not follow the functional paradigm. For example, you cannot easily compose functions using them. My solution has been to create static extension methods that wrap the API (fluent or not) and return the object. I will be demonstrating this approach with WPF on my blog soon (the code is done for that part, but not the supporting material).
I have not found a way round the ‘.’ problem, it does seem an oversight. Here’s an example using an extension method and a type T:
type T() =
member s.get() = () ; s
module Extension =
type T with
static member Get (s:T) = s.get()
Then you can write:
(T())
|> T.Get
|> T.Get
And also (T.Get >> T.Get) is a valid function.
However, it is a pain to write all those extension methods.
The middle ground is to write short “let” statements:
let x1 = new T()
let x2 = x1.get()
let x3 = x2.get()
x3
That is a bit extra code but it has a consistent structure, is strongly typed for the debugger at each step and is nicely formatted. Of course, you can batch statements too:
let x1 = new T()
let x2 = x1.get().get().get().get()
let x3 = x2.get().get().get()
x3
Looks like the indentations were lost. You will need to indent the type and module definitions to allow the code to compile.
Fluent interfaces in F# is a whole different game and most C#/VB fluent interfaces break (lose their fluency) on F#.
I’ve written a blog post about it a couple of months ago.
Matthew –
For the F# code in the last example, you could use:
let rect =
RectangleFluent.Create()
.SetColor(Color.AliceBlue)
.SetHeight(16)
.SetLength(16)
Steve
I agree that pipelining methods is a more functional solution to fluency. Every method call in .net takes this as the first parameter. I wonder if the f# compiler could exploit this to allow for pipeling without the need for extension methods?
Mauricio
Thanks for the link
Luke
I will have to look at why identions are not working in the comments, but i still get an unexpected symbol in binding when i try.
let rect =
RectangleFluent.Create()
.SetColor(Color.AliceBlue)
.SetHeight(16)
.SetLength(16)
Hi Matt,
Yes, there is no reason why I clever F# compiler could not have an extension operator to do just that. I’ve recently discovered that :: is treated as a special case by the compiler as it is actually a discriminated union constructor that expects a tuple but is allowed to act as an infix operated that is curried, so there is a precedent for such special cases.
I’ll suggest it to the product team.
You can find the suggestion on Microsoft Connect here: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=488312.
Hey Luke,
VB10 will have an “implicit line continuation” feature where the goal is that in the 98% case you won’t need to type the underscore anymore.
The case you mention though is one of those situations where it gets trickier for us to remove it, due to the ambiguity of With blocks.
Dim rect = RectangleFluent.Create()
.SetColor(Color.AliceBlue)
.SetHeight(16)
.SetLength(16)
If that code is inside a With block then we could parse it as one statement or 4 statements. We could make a rule to prefer one approach over the other, but that would just get confusing and messy.
Instead what we’ve done is allow the underscore to be implicit *after* the dot:
Dim rect = RectangleFluent.Create().
SetColor(Color.AliceBlue).
SetHeight(16).
SetLength(16)
Definitely not perfect, but we felt this was a reasonable compromise.
Hope that helps,
Jonathan Aneja
Program Manager, VB Compiler