A C# implementation of the CallStream pattern
- by Bertrand Le Roy
Dusan published this interesting post a couple of weeks ago about a novel JavaScript chaining pattern: http://dbj.org/dbj/?p=514 It’s similar to many existing patterns, but the syntax is extraordinarily terse and it provides a new form of friction-free, plugin-less extensibility mechanism. Here’s a JavaScript example from Dusan’s post: CallStream("#container") (find, "div") (attr, "A", 1) (css, "color", "#fff") (logger);
The interesting thing here is that the functions that are being passed as the first argument are arbitrary, they don’t need to be declared as plug-ins. Compare that with a rough jQuery equivalent that could look something like this:
$.fn.logger = function () { /* ... */ }
$("selector")
.find("div")
.attr("A", 1)
.css("color", "#fff")
.logger();
There is also the “each” method in jQuery that achieves something similar, but its syntax is a little more verbose.
Of course, that this pattern can be expressed so easily in JavaScript owes everything to the extraordinary way functions are treated in that language, something Douglas Crockford called “the very best part of JavaScript”.
One of the first things I thought while reading Dusan’s post was how I could adapt that to C#. After all, with Lambdas and delegates, C# also has its first-class functions.
And sure enough, it works really really well. After about ten minutes, I was able to write this:
CallStreamFactory.CallStream
(p => Console.WriteLine("Yay!"))
(Dump, DateTime.Now)
(DumpFooAndBar, new { Foo = 42, Bar = "the answer" })
(p => Console.ReadKey());
Where the Dump function is:
public static void Dump(object options) {
Console.WriteLine(options.ToString());
}
And DumpFooAndBar is:
public static void DumpFooAndBar(dynamic options) {
Console.WriteLine("Foo is {0} and bar is {1}.", options.Foo, options.Bar);
}
So how does this work? Well, it really is very simple. And not. Let’s say it’s not a lot of code, but if you’re like me you might need an Advil after that. First, I defined the signature of the CallStream method as follows:
public delegate CallStream CallStream (Action<object> action, object options = null);
The delegate define a call stream as something that takes an action (a function of the options) and an optional options object and that returns a delegate of its own type. Tricky, but that actually works, a delegate can return its own type.
Then I wrote an implementation of that delegate that calls the action and returns itself:
public static CallStream CallStream (Action<object> action, object options = null) {
action(options);
return CallStream;
}
Pretty nice, eh? Well, yes and no. What we are doing here is to execute a sequence of actions using an interesting novel syntax. But for this to be actually useful, you’d need to build a more specialized call stream factory that comes with some sort of context (like Dusan did in JavaScript).
For example, you could write the following alternate delegate signature that takes a string and returns itself:
public delegate StringCallStream StringCallStream(string message);
And then write the following call stream (notice the currying):
public static StringCallStream CreateDumpCallStream(string dumpPath) {
StringCallStream str = null;
var dump = File.AppendText(dumpPath);
dump.AutoFlush = true;
str = s => {
dump.WriteLine(s);
return str;
};
return str;
}
(I know, I’m not closing that stream; sure; bad, bad Bertrand)
Finally, here’s how you use it:
CallStreamFactory.CreateDumpCallStream(@".\dump.txt")
("Wow, this really works.")
(DateTime.Now.ToLongTimeString())
("And that is all.");
Next step would be to combine this contextual implementation with the one that takes an action parameter and do some really fun stuff.
I’m only scratching the surface here. This pattern could reveal itself to be nothing more than a gratuitous mind-bender or there could be applications that we hardly suspect at this point. In any case, it’s a fun new construct. Or is this nothing new? You tell me… Comments are open :)